From 0245308347deb2a4d9c180d859a89b82e0828bc0 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Sun, 8 Oct 2023 15:42:29 +0800 Subject: [PATCH 01/17] add use selec_head to update startinfo and add some testing case --- Cargo.lock | 1 + block-relayer/src/block_relayer.rs | 4 +- config/src/available_port.rs | 2 +- node/src/node.rs | 14 +- sync/Cargo.toml | 1 + .../block_connector_service.rs | 164 ++++++++++-- sync/src/block_connector/mod.rs | 12 + sync/src/block_connector/write_block_chain.rs | 46 +++- sync/src/sync.rs | 5 +- sync/src/tasks/block_sync_task.rs | 124 ++++++--- sync/src/tasks/inner_sync_task.rs | 4 +- sync/src/tasks/mock.rs | 33 ++- sync/src/tasks/mod.rs | 46 +++- sync/src/tasks/tests.rs | 236 +++++++++++++++++- 14 files changed, 617 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 535327bd7d..28307fda11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10744,6 +10744,7 @@ dependencies = [ "sysinfo", "test-helper", "thiserror", + "timeout-join-handler", "tokio", ] diff --git a/block-relayer/src/block_relayer.rs b/block-relayer/src/block_relayer.rs index d8d791051c..d8978076ae 100644 --- a/block-relayer/src/block_relayer.rs +++ b/block-relayer/src/block_relayer.rs @@ -203,7 +203,9 @@ impl BlockRelayer { ctx: &mut ServiceContext, ) -> Result<()> { let network = ctx.get_shared::()?; - let block_connector_service = ctx.service_ref::()?.clone(); + let block_connector_service = ctx + .service_ref::>()? + .clone(); let txpool = self.txpool.clone(); let metrics = self.metrics.clone(); let fut = async move { diff --git a/config/src/available_port.rs b/config/src/available_port.rs index 588b28ad81..f03bf1af60 100644 --- a/config/src/available_port.rs +++ b/config/src/available_port.rs @@ -57,7 +57,7 @@ fn get_ephemeral_port() -> ::std::io::Result { use std::net::{TcpListener, TcpStream}; // Request a random available port from the OS - let listener = TcpListener::bind(("localhost", 0))?; + let listener = TcpListener::bind(("127.0.0.1", 0))?; let addr = listener.local_addr()?; // Create and accept a connection (which we'll promptly drop) in order to force the port diff --git a/node/src/node.rs b/node/src/node.rs index fd3e7fcf77..34b7cc20a7 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -51,7 +51,7 @@ use starcoin_sync::block_connector::{BlockConnectorService, ExecuteRequest, Rese use starcoin_sync::sync::SyncService; use starcoin_sync::txn_sync::TxnSyncService; use starcoin_sync::verified_rpc_client::VerifiedRpcClient; -use starcoin_txpool::TxPoolActorService; +use starcoin_txpool::{TxPoolActorService, TxPoolService}; use starcoin_types::system_events::{SystemShutdown, SystemStarted}; use starcoin_vm_runtime::metrics::VMMetrics; use std::sync::Arc; @@ -133,7 +133,9 @@ impl ServiceHandler for NodeService { .start_service_sync(GenerateBlockEventPacemaker::service_name()), ), NodeRequest::ResetNode(block_hash) => { - let connect_service = ctx.service_ref::()?.clone(); + let connect_service = ctx + .service_ref::>()? + .clone(); let fut = async move { info!("Prepare to reset node startup info to {}", block_hash); connect_service.send(ResetRequest { block_hash }).await? @@ -147,7 +149,9 @@ impl ServiceHandler for NodeService { .get_shared_sync::>() .expect("Storage must exist."); - let connect_service = ctx.service_ref::()?.clone(); + let connect_service = ctx + .service_ref::>()? + .clone(); let network = ctx.get_shared::()?; let fut = async move { info!("Prepare to re execute block {}", block_hash); @@ -347,7 +351,9 @@ impl NodeService { registry.register::().await?; - registry.register::().await?; + registry + .register::>() + .await?; registry.register::().await?; let block_relayer = registry.register::().await?; diff --git a/sync/Cargo.toml b/sync/Cargo.toml index fdff574ab8..70c429f5cf 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -42,6 +42,7 @@ stest = { workspace = true } stream-task = { workspace = true } sysinfo = { workspace = true } thiserror = { workspace = true } +timeout-join-handler = { workspace = true } [dev-dependencies] hex = { workspace = true } diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index d35d9e4757..f87d1d8c29 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -3,11 +3,12 @@ use crate::block_connector::{ExecuteRequest, ResetRequest, WriteBlockChainService}; use crate::sync::{CheckSyncEvent, SyncService}; -use crate::tasks::{BlockConnectedEvent, BlockDiskCheckEvent}; +use crate::tasks::{BlockConnectedEvent, BlockConnectedFinishEvent, BlockDiskCheckEvent}; use anyhow::{format_err, Result}; use network_api::PeerProvider; -use starcoin_chain_api::{ConnectBlockError, WriteableChainService}; +use starcoin_chain_api::{ConnectBlockError, WriteableChainService, ChainReader}; use starcoin_config::{NodeConfig, G_CRATE_VERSION}; +use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_logger::prelude::*; use starcoin_network::NetworkServiceRef; @@ -17,24 +18,35 @@ use starcoin_service_registry::{ use starcoin_storage::{BlockStore, Storage}; use starcoin_sync_api::PeerNewBlock; use starcoin_txpool::TxPoolService; +use starcoin_txpool_api::TxPoolSyncService; +#[cfg(test)] +use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::ExecutedBlock; use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::{MinedBlock, SyncStatusChangeEvent, SystemShutdown}; use std::sync::Arc; use sysinfo::{DiskExt, System, SystemExt}; +#[cfg(test)] +use super::CheckBlockConnectorHashValue; const DISK_CHECKPOINT_FOR_PANIC: u64 = 1024 * 1024 * 1024 * 3; const DISK_CHECKPOINT_FOR_WARN: u64 = 1024 * 1024 * 1024 * 5; -pub struct BlockConnectorService { - chain_service: WriteBlockChainService, +pub struct BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ + chain_service: WriteBlockChainService, sync_status: Option, config: Arc, } -impl BlockConnectorService { +impl BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ pub fn new( - chain_service: WriteBlockChainService, + chain_service: WriteBlockChainService, config: Arc, ) -> Self { Self { @@ -51,6 +63,10 @@ impl BlockConnectorService { } } + pub fn chain_head_id(&self) -> HashValue { + self.chain_service.get_main().status().head.id() + } + pub fn check_disk_space(&mut self) -> Option> { if System::IS_SUPPORTED { let mut sys = System::new_all(); @@ -97,11 +113,17 @@ impl BlockConnectorService { } } -impl ServiceFactory for BlockConnectorService { - fn create(ctx: &mut ServiceContext) -> Result { +impl ServiceFactory + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ + fn create( + ctx: &mut ServiceContext>, + ) -> Result> { let config = ctx.get_shared::>()?; let bus = ctx.bus_ref().clone(); - let txpool = ctx.get_shared::()?; + let txpool = ctx.get_shared::()?; let storage = ctx.get_shared::>()?; let startup_info = storage .get_startup_info()? @@ -120,7 +142,10 @@ impl ServiceFactory for BlockConnectorService { } } -impl ActorService for BlockConnectorService { +impl ActorService for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn started(&mut self, ctx: &mut ServiceContext) -> Result<()> { //TODO figure out a more suitable value. ctx.set_mailbox_capacity(1024); @@ -141,11 +166,15 @@ impl ActorService for BlockConnectorService { } } -impl EventHandler for BlockConnectorService { +impl EventHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle_event( &mut self, _: BlockDiskCheckEvent, - ctx: &mut ServiceContext, + ctx: &mut ServiceContext>, ) { if let Some(res) = self.check_disk_space() { match res { @@ -161,23 +190,74 @@ impl EventHandler for BlockConnectorService { } } -impl EventHandler for BlockConnectorService { +impl EventHandler + for BlockConnectorService +{ fn handle_event( &mut self, msg: BlockConnectedEvent, - _ctx: &mut ServiceContext, + ctx: &mut ServiceContext>, ) { //because this block has execute at sync task, so just try connect to select head chain. //TODO refactor connect and execute let block = msg.block; - if let Err(e) = self.chain_service.try_connect(block) { - error!("Process connected block error: {:?}", e); + let feedback = msg.feedback; + + match msg.action { + crate::tasks::BlockConnectAction::ConnectNewBlock => { + if let Err(e) = self.chain_service.try_connect(block) { + error!("Process connected new block from sync error: {:?}", e); + } + } + crate::tasks::BlockConnectAction::ConnectExecutedBlock => { + if let Err(e) = self.chain_service.switch_new_main(block.header().id(), ctx) { + error!("Process connected executed block from sync error: {:?}", e); + } + } } + + feedback.map(|f| f.unbounded_send(BlockConnectedFinishEvent)); } } -impl EventHandler for BlockConnectorService { +#[cfg(test)] +impl EventHandler + for BlockConnectorService +{ + fn handle_event( + &mut self, + msg: BlockConnectedEvent, + ctx: &mut ServiceContext>, + ) { + //because this block has execute at sync task, so just try connect to select head chain. + //TODO refactor connect and execute + + let block = msg.block; + let feedback = msg.feedback; + + match msg.action { + crate::tasks::BlockConnectAction::ConnectNewBlock => { + if let Err(e) = self.chain_service.apply_failed(block) { + error!("Process connected new block from sync error: {:?}", e); + } + } + crate::tasks::BlockConnectAction::ConnectExecutedBlock => { + if let Err(e) = self.chain_service.switch_new_main(block.header().id(), ctx) { + error!("Process connected executed block from sync error: {:?}", e); + } + } + } + + feedback.map(|f| f.unbounded_send(BlockConnectedFinishEvent)); + } +} + +impl EventHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle_event(&mut self, msg: MinedBlock, _ctx: &mut ServiceContext) { let MinedBlock(new_block) = msg; let id = new_block.header().id(); @@ -192,13 +272,21 @@ impl EventHandler for BlockConnectorService { } } -impl EventHandler for BlockConnectorService { +impl EventHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle_event(&mut self, msg: SyncStatusChangeEvent, _ctx: &mut ServiceContext) { self.sync_status = Some(msg.0); } } -impl EventHandler for BlockConnectorService { +impl EventHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle_event(&mut self, msg: PeerNewBlock, ctx: &mut ServiceContext) { if !self.is_synced() { debug!("[connector] Ignore PeerNewBlock event because the node has not been synchronized yet."); @@ -257,22 +345,52 @@ impl EventHandler for BlockConnectorService { } } -impl ServiceHandler for BlockConnectorService { +impl ServiceHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle( &mut self, msg: ResetRequest, - _ctx: &mut ServiceContext, + _ctx: &mut ServiceContext>, ) -> Result<()> { self.chain_service.reset(msg.block_hash) } } -impl ServiceHandler for BlockConnectorService { +impl ServiceHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ fn handle( &mut self, msg: ExecuteRequest, - _ctx: &mut ServiceContext, + _ctx: &mut ServiceContext>, ) -> Result { self.chain_service.execute(msg.block) } } + +#[cfg(test)] +impl ServiceHandler + for BlockConnectorService +where + TransactionPoolServiceT: TxPoolSyncService + 'static, +{ + fn handle( + &mut self, + msg: CheckBlockConnectorHashValue, + _ctx: &mut ServiceContext>, + ) -> Result<()> { + if self.chain_service.get_main().status().head().id() == msg.head_hash { + info!("the branch in chain service is the same as target's branch"); + return Ok(()); + } + info!("mock branch in chain service is not the same as target's branch"); + bail!("blockchain in chain service is not the same as target!"); + } +} + + diff --git a/sync/src/block_connector/mod.rs b/sync/src/block_connector/mod.rs index 05b7cfd2b2..c95df5a5a6 100644 --- a/sync/src/block_connector/mod.rs +++ b/sync/src/block_connector/mod.rs @@ -40,3 +40,15 @@ pub struct ExecuteRequest { impl ServiceRequest for ExecuteRequest { type Response = anyhow::Result; } + +#[cfg(test)] +#[derive(Debug, Clone)] +pub struct CheckBlockConnectorHashValue { + pub head_hash: HashValue, +} + +#[cfg(test)] +impl ServiceRequest for CheckBlockConnectorHashValue { + type Response = anyhow::Result<()>; +} + diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index c22ff42408..cab825e583 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -10,7 +10,7 @@ use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_logger::prelude::*; use starcoin_service_registry::bus::{Bus, BusService}; -use starcoin_service_registry::ServiceRef; +use starcoin_service_registry::{ServiceContext, ServiceRef}; use starcoin_storage::Store; use starcoin_txpool_api::TxPoolSyncService; use starcoin_types::block::BlockInfo; @@ -22,6 +22,8 @@ use starcoin_types::{ use std::fmt::Formatter; use std::sync::Arc; +use super::BlockConnectorService; + const MAX_ROLL_BACK_BLOCK: usize = 10; pub struct WriteBlockChainService

@@ -93,15 +95,15 @@ where } } -impl

WriteBlockChainService

+impl WriteBlockChainService where - P: TxPoolSyncService + 'static, + TransactionPoolServiceT: TxPoolSyncService + 'static, { pub fn new( config: Arc, startup_info: StartupInfo, storage: Arc, - txpool: P, + txpool: TransactionPoolServiceT, bus: ServiceRef, vm_metrics: Option, ) -> Result { @@ -169,6 +171,42 @@ where &self.main } + #[cfg(test)] + pub fn apply_failed(&mut self, block: Block) -> Result<()> { + use anyhow::bail; + use starcoin_chain::verifier::FullVerifier; + + // apply but no connection + let verified_block = self.main.verify_with_verifier::(block)?; + let _executed_block = self.main.execute(verified_block)?; + + bail!("failed to apply for tesing the connection later!"); + } + + // for sync task to connect to its chain, if chain's total difficulties is larger than the main + // switch by: + // 1, update the startup info + // 2, broadcast the new header + pub fn switch_new_main( + &mut self, + new_head_block: HashValue, + _ctx: &mut ServiceContext>, + ) -> Result<()> + where + TransactionPoolServiceT: TxPoolSyncService, + { + let new_branch = BlockChain::new( + self.config.net().time_service(), + new_head_block, + self.storage.clone(), + self.vm_metrics.clone(), + )?; + + self.select_head(new_branch)?; + + Ok(()) + } + pub fn select_head(&mut self, new_branch: BlockChain) -> Result<()> { let executed_block = new_branch.head_block(); let main_total_difficulty = self.main.get_total_difficulty()?; diff --git a/sync/src/sync.rs b/sync/src/sync.rs index dd4bb57f3c..abfd0ddd6b 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -26,6 +26,7 @@ use starcoin_sync_api::{ PeerScoreRequest, PeerScoreResponse, SyncCancelRequest, SyncProgressReport, SyncProgressRequest, SyncServiceHandler, SyncStartRequest, SyncStatusRequest, SyncTarget, }; +use starcoin_txpool::TxPoolService; use starcoin_types::block::BlockIdAndNumber; use starcoin_types::startup_info::ChainStatus; use starcoin_types::sync_status::SyncStatus; @@ -144,7 +145,9 @@ impl SyncService { let network = ctx.get_shared::()?; let storage = self.storage.clone(); let self_ref = ctx.self_ref(); - let connector_service = ctx.service_ref::()?.clone(); + let connector_service = ctx + .service_ref::>()? + .clone(); let config = self.config.clone(); let peer_score_metrics = self.peer_score_metrics.clone(); let sync_metrics = self.metrics.clone(); diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 57f6703a9d..fbb517930c 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -18,8 +18,11 @@ use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use std::collections::HashMap; use std::sync::Arc; +use std::time::Duration; use stream_task::{CollectorState, TaskError, TaskResultCollector, TaskState}; +use super::{BlockConnectAction, BlockConnectedFinishEvent}; + #[derive(Clone, Debug)] pub struct SyncBlockData { pub(crate) block: Block, @@ -217,6 +220,69 @@ where self.apply_block(block, None) } + fn notify_connected_block( + &mut self, + block: Block, + block_info: BlockInfo, + action: BlockConnectAction, + state: CollectorState, + ) -> Result { + let total_difficulty = block_info.get_total_difficulty(); + + // if the new block's total difficulty is smaller than the current, + // do nothing because we do not need to update the current chain in any other services. + if total_difficulty <= self.current_block_info.total_difficulty { + return Ok(state); // nothing to do + } + + // only try connect block when sync chain total_difficulty > node's current chain. + + // first, create the sender and receiver for ensuring that + // the last block is connected before the next synchronization is triggered. + // if the block is not the last one, we do not want to do this. + let (sender, mut receiver) = match state { + CollectorState::Enough => { + let (s, r) = futures::channel::mpsc::unbounded::(); + (Some(s), Some(r)) + } + CollectorState::Need => (None, None), + }; + + // second, construct the block connect event. + let block_connect_event = BlockConnectedEvent { + block, + feedback: sender, + action, + }; + + // third, broadcast it. + if let Err(e) = self.event_handle.handle(block_connect_event.clone()) { + error!( + "Send BlockConnectedEvent error: {:?}, block_id: {}", + e, + block_info.block_id() + ); + } + + // finally, if it is the last one, wait for the last block to be processed. + if block_connect_event.feedback.is_some() && receiver.is_some() { + let mut count = 0; + while count < 3 { + count += 1; + match receiver.as_mut().unwrap().try_next() { + Ok(_) => { + break; + } + Err(_) => { + info!("Waiting for last block to be processed"); + async_std::task::block_on(async_std::task::sleep(Duration::from_secs(10))); + } + } + } + } + return Ok(state); + } + fn apply_block(&mut self, block: Block, peer_id: Option) -> Result<()> { if let Some((_failed_block, pre_peer_id, err, version)) = self .chain @@ -293,59 +359,53 @@ where fn collect(&mut self, item: SyncBlockData) -> Result { let (block, block_info, peer_id) = item.into(); - let block_id = block.id(); let timestamp = block.header().timestamp(); - let block_info = match block_info { + let (block_info, action) = match block_info { Some(block_info) => { //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. //So, we just need to update chain and continue self.chain.connect(ExecutedBlock { - block, + block: block.clone(), block_info: block_info.clone(), })?; - block_info + (block_info, BlockConnectAction::ConnectExecutedBlock) } None => { self.apply_block(block.clone(), peer_id)?; self.chain.time_service().adjust(timestamp); - let block_info = self.chain.status().info; - let total_difficulty = block_info.get_total_difficulty(); - // only try connect block when sync chain total_difficulty > node's current chain. - if total_difficulty > self.current_block_info.total_difficulty { - if let Err(e) = self.event_handle.handle(BlockConnectedEvent { block }) { - error!( - "Send BlockConnectedEvent error: {:?}, block_id: {}", - e, block_id - ); - } - } - block_info + ( + self.chain.status().info, + BlockConnectAction::ConnectNewBlock, + ) } }; //verify target - if block_info.block_accumulator_info.num_leaves - == self.target.block_info.block_accumulator_info.num_leaves - { - if block_info != self.target.block_info { - Err(TaskError::BreakError( - RpcVerifyError::new_with_peers( - self.target.peers.clone(), - format!( + let state: Result = + if block_info.block_accumulator_info.num_leaves + == self.target.block_info.block_accumulator_info.num_leaves + { + if block_info != self.target.block_info { + Err(TaskError::BreakError( + RpcVerifyError::new_with_peers( + self.target.peers.clone(), + format!( "Verify target error, expect target: {:?}, collect target block_info:{:?}", self.target.block_info, block_info ), + ) + .into(), ) - .into(), - ) - .into()) + .into()) + } else { + Ok(CollectorState::Enough) + } } else { - Ok(CollectorState::Enough) - } - } else { - Ok(CollectorState::Need) - } + Ok(CollectorState::Need) + }; + + self.notify_connected_block(block, block_info, action, state?) } fn finish(self) -> Result { diff --git a/sync/src/tasks/inner_sync_task.rs b/sync/src/tasks/inner_sync_task.rs index 7552656417..dc80fc4415 100644 --- a/sync/src/tasks/inner_sync_task.rs +++ b/sync/src/tasks/inner_sync_task.rs @@ -117,8 +117,8 @@ where ) .and_then(move |(ancestor, accumulator), event_handle| { let check_local_store = - ancestor_block_info.total_difficulty < current_block_info.total_difficulty; - + ancestor_block_info.total_difficulty <= current_block_info.total_difficulty; + let block_sync_task = BlockSyncTask::new( accumulator, ancestor, diff --git a/sync/src/tasks/mock.rs b/sync/src/tasks/mock.rs index 5f5c66034d..c5c47d1099 100644 --- a/sync/src/tasks/mock.rs +++ b/sync/src/tasks/mock.rs @@ -4,7 +4,7 @@ use crate::tasks::{ BlockConnectedEvent, BlockFetcher, BlockIdFetcher, BlockInfoFetcher, PeerOperator, SyncFetcher, }; -use anyhow::{format_err, Context, Result}; +use anyhow::{format_err, Context, Ok, Result}; use async_std::task::JoinHandle; use futures::channel::mpsc::UnboundedReceiver; use futures::future::BoxFuture; @@ -14,6 +14,7 @@ use network_api::messages::NotificationMessage; use network_api::{PeerId, PeerInfo, PeerSelector, PeerStrategy}; use network_p2p_core::{NetRpcError, RpcErrorCode}; use rand::Rng; +use starcoin_account_api::AccountInfo; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::BlockChain; use starcoin_chain_api::ChainReader; @@ -21,8 +22,10 @@ use starcoin_chain_mock::MockChain; use starcoin_config::ChainNetwork; use starcoin_crypto::HashValue; use starcoin_network_rpc_api::G_RPC_INFO; +use starcoin_storage::Storage; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; +use starcoin_types::startup_info::ChainInfo; use std::sync::Arc; use std::time::Duration; @@ -162,6 +165,34 @@ impl SyncNodeMocker { )) } + pub fn new_with_storage( + net: ChainNetwork, + storage: Arc, + chain_info: ChainInfo, + miner: AccountInfo, + delay_milliseconds: u64, + random_error_percent: u32, + ) -> Result { + let chain = + MockChain::new_with_storage(net, storage, chain_info.head().id().clone(), miner)?; + let peer_id = PeerId::random(); + let peer_info = PeerInfo::new( + peer_id.clone(), + chain.chain_info(), + NotificationMessage::protocols(), + G_RPC_INFO.clone().into_protocols(), + None, + ); + let peer_selector = PeerSelector::new(vec![peer_info], PeerStrategy::default(), None); + Ok(Self::new_inner( + peer_id, + chain, + ErrorStrategy::Timeout(delay_milliseconds), + random_error_percent, + peer_selector, + )) + } + pub fn new_with_strategy( net: ChainNetwork, error_strategy: ErrorStrategy, diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 1ed2424924..60bb1079c5 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +use crate::block_connector::BlockConnectorService; use crate::tasks::block_sync_task::SyncBlockData; use crate::tasks::inner_sync_task::InnerSyncTask; use crate::verified_rpc_client::{RpcVerifyError, VerifiedRpcClient}; @@ -19,6 +20,9 @@ use starcoin_service_registry::{ActorService, EventHandler, ServiceRef}; use starcoin_storage::Store; use starcoin_sync_api::SyncTarget; use starcoin_time_service::TimeService; +use starcoin_txpool::TxPoolService; +#[cfg(test)] +use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use starcoin_types::startup_info::ChainStatus; use starcoin_types::U256; @@ -380,11 +384,22 @@ impl BlockLocalStore for Arc { } } +#[derive(Clone, Debug)] +pub enum BlockConnectAction { + ConnectNewBlock, + ConnectExecutedBlock, +} + #[derive(Clone, Debug)] pub struct BlockConnectedEvent { pub block: Block, + pub feedback: Option>, + pub action: BlockConnectAction, } +#[derive(Clone, Debug)] +pub struct BlockConnectedFinishEvent; + #[derive(Clone, Debug)] pub struct BlockDiskCheckEvent {} @@ -392,9 +407,7 @@ pub trait BlockConnectedEventHandle: Send + Clone + std::marker::Unpin { fn handle(&mut self, event: BlockConnectedEvent) -> Result<()>; } -impl BlockConnectedEventHandle for ServiceRef -where - S: ActorService + EventHandler, +impl BlockConnectedEventHandle for ServiceRef> { fn handle(&mut self, event: BlockConnectedEvent) -> Result<()> { self.notify(event)?; @@ -402,6 +415,15 @@ where } } +#[cfg(test)] +impl BlockConnectedEventHandle for ServiceRef> +{ + fn handle(&mut self, event: BlockConnectedEvent) -> Result<()> { + self.notify(event)?; + return Ok(()); + } +} + #[derive(Clone, Debug)] pub struct AncestorEvent { pub ancestor: BlockIdAndNumber, @@ -459,6 +481,24 @@ impl BlockConnectedEventHandle for UnboundedSender { } } +#[derive(Debug, Clone)] +pub struct BlockConnectEventHandleMock { + sender: UnboundedSender, +} + +impl BlockConnectEventHandleMock { + pub fn new(sender: UnboundedSender) -> Result { + Ok(Self { sender }) + } +} + +impl BlockConnectedEventHandle for BlockConnectEventHandleMock { + fn handle(&mut self, event: BlockConnectedEvent) -> Result<()> { + self.sender.start_send(event)?; + Ok(()) + } +} + pub struct ExtSyncTaskErrorHandle where F: SyncFetcher + 'static, diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index 06206f227e..2e357ab652 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #![allow(clippy::integer_arithmetic)] +use crate::block_connector::{BlockConnectorService, CheckBlockConnectorHashValue}; use crate::tasks::block_sync_task::SyncBlockData; use crate::tasks::mock::{ErrorStrategy, MockBlockIdFetcher, SyncNodeMocker}; use crate::tasks::{ @@ -9,30 +10,37 @@ use crate::tasks::{ BlockCollector, BlockFetcher, BlockLocalStore, BlockSyncTask, FindAncestorTask, SyncFetcher, }; use crate::verified_rpc_client::RpcVerifyError; -use anyhow::Context; use anyhow::{format_err, Result}; +use anyhow::{Context, Ok}; use futures::channel::mpsc::unbounded; use futures::future::BoxFuture; use futures::FutureExt; use futures_timer::Delay; use network_api::{PeerId, PeerInfo, PeerSelector, PeerStrategy}; use pin_utils::core_reexport::time::Duration; +use starcoin_account_api::AccountInfo; use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_accumulator::tree_store::mock::MockAccumulatorStore; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::BlockChain; use starcoin_chain_api::ChainReader; use starcoin_chain_mock::MockChain; -use starcoin_config::{BuiltinNetworkID, ChainNetwork}; +use starcoin_config::{BuiltinNetworkID, ChainNetwork, NodeConfig}; use starcoin_crypto::HashValue; use starcoin_genesis::Genesis; +use starcoin_genesis::Genesis as StarcoinGenesis; use starcoin_logger::prelude::*; -use starcoin_storage::BlockStore; +use starcoin_service_registry::{RegistryAsyncService, RegistryService, ServiceRef, ActorService}; +use starcoin_storage::{BlockStore, Storage}; use starcoin_sync_api::SyncTarget; +use starcoin_txpool::{TxPoolActorService, TxPoolService}; +use starcoin_txpool_api::TxPoolSyncService; +use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::{ block::{Block, BlockBody, BlockHeaderBuilder, BlockIdAndNumber, BlockInfo}, U256, }; +use stest::actix_export::System; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use stream_task::{ @@ -40,6 +48,8 @@ use stream_task::{ }; use test_helper::DummyNetworkService; +use super::BlockConnectedEvent; + #[stest::test(timeout = 120)] pub async fn test_full_sync_new_node() -> Result<()> { let net1 = ChainNetwork::new_builtin(BuiltinNetworkID::Test); @@ -984,3 +994,223 @@ async fn test_sync_target() { assert_eq!(target.target_id.number(), low_chain_info.head().number()); assert_eq!(target.target_id.id(), low_chain_info.head().id()); } + +fn sync_block_in_async_connection( + mut target_node: Arc, + local_node: Arc, + storage: Arc, + block_count: u64, +) -> Result> { + Arc::get_mut(&mut target_node) + .unwrap() + .produce_block(block_count)?; + let target = target_node.sync_target(); + let target_id = target.target_id.id(); + + let (sender, mut receiver) = futures::channel::mpsc::unbounded::(); + let thread_local_node = local_node.clone(); + + let process_block = move || { + let mut chain = MockChain::new_with_storage( + thread_local_node.chain_mocker.net().clone(), + storage.clone(), + thread_local_node + .chain_mocker + .head() + .status() + .head + .id() + .clone(), + thread_local_node.chain_mocker.miner().clone(), + ) + .unwrap(); + loop { + match receiver.try_next() { + std::result::Result::Ok(result) => match result { + Some(event) => { + chain + .select_head(event.block) + .expect("select head must be successful"); + if event.feedback.is_some() { + event + .feedback + .unwrap() + .unbounded_send(super::BlockConnectedFinishEvent) + .unwrap(); + assert_eq!(target_id, chain.head().status().head.id()); + break; + } + } + None => break, + }, + Err(_) => (), + } + } + }; + let handle = std::thread::spawn(process_block); + + let current_block_header = local_node.chain().current_header(); + let storage = local_node.chain().get_storage(); + + let local_net = local_node.chain_mocker.net(); + let (local_ancestor_sender, _local_ancestor_receiver) = unbounded(); + + let (sync_task, _task_handle, task_event_counter) = full_sync_task( + current_block_header.id(), + target.clone(), + false, + local_net.time_service(), + storage.clone(), + sender.clone(), + target_node.clone(), + local_ancestor_sender, + DummyNetworkService::default(), + 15, + None, + None, + )?; + let branch = async_std::task::block_on(sync_task)?; + assert_eq!(branch.current_header().id(), target.target_id.id()); + + handle.join().unwrap(); + + let reports = task_event_counter.get_reports(); + reports + .iter() + .for_each(|report| debug!("reports: {}", report)); + + Ok(target_node) +} + +#[stest::test] +async fn test_sync_block_in_async_connection() -> Result<()> { + let net = ChainNetwork::new_builtin(BuiltinNetworkID::Test); + let mut target_node = Arc::new(SyncNodeMocker::new(net.clone(), 1, 0)?); + + let (storage, chain_info, _) = + Genesis::init_storage_for_test(&net).expect("init storage by genesis fail."); + let local_node = Arc::new(SyncNodeMocker::new_with_storage( + net.clone(), + storage.clone(), + chain_info.clone(), + AccountInfo::random(), + 1, + 0, + )?); + + target_node = + sync_block_in_async_connection(target_node, local_node.clone(), storage.clone(), 10)?; + _ = sync_block_in_async_connection(target_node, local_node.clone(), storage.clone(), 20)?; + + Ok(()) +} + +fn sync_block_in_block_connection_service_mock( + mut target_node: Arc, + local_node: Arc, + registry: &ServiceRef, + block_count: u64, +) -> Result> { + Arc::get_mut(&mut target_node) + .unwrap() + .produce_block(block_count)?; + loop { + let target = target_node.sync_target(); + + let storage = local_node.chain().get_storage(); + let startup_info = storage + .get_startup_info()? + .ok_or_else(|| format_err!("Startup info should exist."))?; + let current_block_id = startup_info.main; + + let local_net = local_node.chain_mocker.net(); + let (local_ancestor_sender, _local_ancestor_receiver) = unbounded(); + + let (sync_task, _task_handle, task_event_counter) = full_sync_task( + current_block_id, + target.clone(), + false, + local_net.time_service(), + storage.clone(), + async_std::task::block_on(registry.service_ref::>())?.clone(), + target_node.clone(), + local_ancestor_sender, + DummyNetworkService::default(), + 15, + None, + None, + )?; + let branch = async_std::task::block_on(sync_task)?; + info!("checking branch in sync service is the same as target's branch"); + assert_eq!(branch.current_header().id(), target.target_id.id()); + + let block_connector_service = async_std::task::block_on(registry.service_ref::>())?.clone(); + let result = async_std::task::block_on(block_connector_service.send(CheckBlockConnectorHashValue { + head_hash: target.target_id.id(), + }))?; + if result.is_ok() { + break; + } + let reports = task_event_counter.get_reports(); + reports + .iter() + .for_each(|report| debug!("reports: {}", report)); + } + + Ok(target_node) +} + +#[stest::test] +async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { + let config = Arc::new(NodeConfig::random_for_test()); + let (storage, chain_info, _) = StarcoinGenesis::init_storage_for_test(config.net()) + .expect("init storage by genesis fail."); + + let target_node = Arc::new(SyncNodeMocker::new(config.net().clone(), 1, 0)?); + let local_node = Arc::new(SyncNodeMocker::new_with_storage( + config.net().clone(), + storage.clone(), + chain_info.clone(), + AccountInfo::random(), + 1, + 0, + )?); + + let (registry_sender, registry_receiver) = async_std::channel::unbounded(); + + let _handle = timeout_join_handler::spawn(move|| { + let system = System::with_tokio_rt(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .on_thread_stop(|| debug!("main thread stopped")) + .thread_name("main") + .build() + .expect("failed to create tokio runtime for main") + }); + async_std::task::block_on(async { + let registry = RegistryService::launch(); + + registry.put_shared(config.clone()).await.unwrap(); + registry.put_shared(storage.clone()).await.unwrap(); + registry.put_shared(MockTxPoolService::new()).await.unwrap(); + + Delay::new(Duration::from_secs(2)).await; + + registry + .register::>() + .await.unwrap(); + + registry_sender.send(registry).await.unwrap(); + + }); + + system.run().unwrap(); + }); + + let registry = registry_receiver.recv().await.unwrap(); + + let target_node = sync_block_in_block_connection_service_mock(target_node, local_node.clone(), ®istry, 10)?; + _ = sync_block_in_block_connection_service_mock(target_node, local_node.clone(), ®istry, 20)?; + + Ok(()) +} From 33e65bbacc06db7508836f242d0a1e09b21b6ed9 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Sun, 8 Oct 2023 16:21:02 +0800 Subject: [PATCH 02/17] fix fmt and clippy --- .../block_connector_service.rs | 25 +++--- sync/src/block_connector/mod.rs | 1 - sync/src/tasks/block_sync_task.rs | 6 +- sync/src/tasks/inner_sync_task.rs | 2 +- sync/src/tasks/mock.rs | 3 +- sync/src/tasks/mod.rs | 8 +- sync/src/tasks/tests.rs | 77 +++++++++++-------- 7 files changed, 62 insertions(+), 60 deletions(-) diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index f87d1d8c29..11e1addffa 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -1,12 +1,16 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +#[cfg(test)] +use super::CheckBlockConnectorHashValue; use crate::block_connector::{ExecuteRequest, ResetRequest, WriteBlockChainService}; use crate::sync::{CheckSyncEvent, SyncService}; use crate::tasks::{BlockConnectedEvent, BlockConnectedFinishEvent, BlockDiskCheckEvent}; +#[cfg(test)] +use anyhow::bail; use anyhow::{format_err, Result}; use network_api::PeerProvider; -use starcoin_chain_api::{ConnectBlockError, WriteableChainService, ChainReader}; +use starcoin_chain_api::{ChainReader, ConnectBlockError, WriteableChainService}; use starcoin_config::{NodeConfig, G_CRATE_VERSION}; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; @@ -26,8 +30,6 @@ use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::{MinedBlock, SyncStatusChangeEvent, SystemShutdown}; use std::sync::Arc; use sysinfo::{DiskExt, System, SystemExt}; -#[cfg(test)] -use super::CheckBlockConnectorHashValue; const DISK_CHECKPOINT_FOR_PANIC: u64 = 1024 * 1024 * 1024 * 3; const DISK_CHECKPOINT_FOR_WARN: u64 = 1024 * 1024 * 1024 * 5; @@ -190,9 +192,7 @@ where } } -impl EventHandler - for BlockConnectorService -{ +impl EventHandler for BlockConnectorService { fn handle_event( &mut self, msg: BlockConnectedEvent, @@ -222,9 +222,7 @@ impl EventHandler } #[cfg(test)] -impl EventHandler - for BlockConnectorService -{ +impl EventHandler for BlockConnectorService { fn handle_event( &mut self, msg: BlockConnectedEvent, @@ -386,11 +384,10 @@ where ) -> Result<()> { if self.chain_service.get_main().status().head().id() == msg.head_hash { info!("the branch in chain service is the same as target's branch"); - return Ok(()); + Ok(()) + } else { + info!("mock branch in chain service is not the same as target's branch"); + bail!("blockchain in chain service is not the same as target!"); } - info!("mock branch in chain service is not the same as target's branch"); - bail!("blockchain in chain service is not the same as target!"); } } - - diff --git a/sync/src/block_connector/mod.rs b/sync/src/block_connector/mod.rs index c95df5a5a6..72ea8d3560 100644 --- a/sync/src/block_connector/mod.rs +++ b/sync/src/block_connector/mod.rs @@ -51,4 +51,3 @@ pub struct CheckBlockConnectorHashValue { impl ServiceRequest for CheckBlockConnectorHashValue { type Response = anyhow::Result<()>; } - diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index fbb517930c..3fe42d66cc 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -266,9 +266,9 @@ where // finally, if it is the last one, wait for the last block to be processed. if block_connect_event.feedback.is_some() && receiver.is_some() { - let mut count = 0; + let mut count: i32 = 0; while count < 3 { - count += 1; + count = count.saturating_add(1); match receiver.as_mut().unwrap().try_next() { Ok(_) => { break; @@ -280,7 +280,7 @@ where } } } - return Ok(state); + Ok(state) } fn apply_block(&mut self, block: Block, peer_id: Option) -> Result<()> { diff --git a/sync/src/tasks/inner_sync_task.rs b/sync/src/tasks/inner_sync_task.rs index dc80fc4415..8d0f70e953 100644 --- a/sync/src/tasks/inner_sync_task.rs +++ b/sync/src/tasks/inner_sync_task.rs @@ -118,7 +118,7 @@ where .and_then(move |(ancestor, accumulator), event_handle| { let check_local_store = ancestor_block_info.total_difficulty <= current_block_info.total_difficulty; - + let block_sync_task = BlockSyncTask::new( accumulator, ancestor, diff --git a/sync/src/tasks/mock.rs b/sync/src/tasks/mock.rs index c5c47d1099..6b9ddb3296 100644 --- a/sync/src/tasks/mock.rs +++ b/sync/src/tasks/mock.rs @@ -173,8 +173,7 @@ impl SyncNodeMocker { delay_milliseconds: u64, random_error_percent: u32, ) -> Result { - let chain = - MockChain::new_with_storage(net, storage, chain_info.head().id().clone(), miner)?; + let chain = MockChain::new_with_storage(net, storage, chain_info.head().id(), miner)?; let peer_id = PeerId::random(); let peer_info = PeerInfo::new( peer_id.clone(), diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 60bb1079c5..7577beaa00 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -407,8 +407,7 @@ pub trait BlockConnectedEventHandle: Send + Clone + std::marker::Unpin { fn handle(&mut self, event: BlockConnectedEvent) -> Result<()>; } -impl BlockConnectedEventHandle for ServiceRef> -{ +impl BlockConnectedEventHandle for ServiceRef> { fn handle(&mut self, event: BlockConnectedEvent) -> Result<()> { self.notify(event)?; Ok(()) @@ -416,11 +415,10 @@ impl BlockConnectedEventHandle for ServiceRef> -{ +impl BlockConnectedEventHandle for ServiceRef> { fn handle(&mut self, event: BlockConnectedEvent) -> Result<()> { self.notify(event)?; - return Ok(()); + Ok(()) } } diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index 2e357ab652..bfa9b02e51 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -30,19 +30,17 @@ use starcoin_crypto::HashValue; use starcoin_genesis::Genesis; use starcoin_genesis::Genesis as StarcoinGenesis; use starcoin_logger::prelude::*; -use starcoin_service_registry::{RegistryAsyncService, RegistryService, ServiceRef, ActorService}; +use starcoin_service_registry::{RegistryAsyncService, RegistryService, ServiceRef}; use starcoin_storage::{BlockStore, Storage}; use starcoin_sync_api::SyncTarget; -use starcoin_txpool::{TxPoolActorService, TxPoolService}; -use starcoin_txpool_api::TxPoolSyncService; use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::{ block::{Block, BlockBody, BlockHeaderBuilder, BlockIdAndNumber, BlockInfo}, U256, }; -use stest::actix_export::System; use std::collections::HashMap; use std::sync::{Arc, Mutex}; +use stest::actix_export::System; use stream_task::{ DefaultCustomErrorHandle, Generator, TaskError, TaskEventCounterHandle, TaskGenerator, }; @@ -1014,19 +1012,13 @@ fn sync_block_in_async_connection( let mut chain = MockChain::new_with_storage( thread_local_node.chain_mocker.net().clone(), storage.clone(), - thread_local_node - .chain_mocker - .head() - .status() - .head - .id() - .clone(), + thread_local_node.chain_mocker.head().status().head.id(), thread_local_node.chain_mocker.miner().clone(), ) .unwrap(); loop { - match receiver.try_next() { - std::result::Result::Ok(result) => match result { + if let std::result::Result::Ok(result) = receiver.try_next() { + match result { Some(event) => { chain .select_head(event.block) @@ -1042,8 +1034,7 @@ fn sync_block_in_async_connection( } } None => break, - }, - Err(_) => (), + } } } }; @@ -1061,7 +1052,7 @@ fn sync_block_in_async_connection( false, local_net.time_service(), storage.clone(), - sender.clone(), + sender, target_node.clone(), local_ancestor_sender, DummyNetworkService::default(), @@ -1090,9 +1081,9 @@ async fn test_sync_block_in_async_connection() -> Result<()> { let (storage, chain_info, _) = Genesis::init_storage_for_test(&net).expect("init storage by genesis fail."); let local_node = Arc::new(SyncNodeMocker::new_with_storage( - net.clone(), + net, storage.clone(), - chain_info.clone(), + chain_info, AccountInfo::random(), 1, 0, @@ -1100,7 +1091,7 @@ async fn test_sync_block_in_async_connection() -> Result<()> { target_node = sync_block_in_async_connection(target_node, local_node.clone(), storage.clone(), 10)?; - _ = sync_block_in_async_connection(target_node, local_node.clone(), storage.clone(), 20)?; + _ = sync_block_in_async_connection(target_node, local_node, storage, 20)?; Ok(()) } @@ -1119,20 +1110,23 @@ fn sync_block_in_block_connection_service_mock( let storage = local_node.chain().get_storage(); let startup_info = storage - .get_startup_info()? - .ok_or_else(|| format_err!("Startup info should exist."))?; + .get_startup_info()? + .ok_or_else(|| format_err!("Startup info should exist."))?; let current_block_id = startup_info.main; let local_net = local_node.chain_mocker.net(); let (local_ancestor_sender, _local_ancestor_receiver) = unbounded(); - + let (sync_task, _task_handle, task_event_counter) = full_sync_task( current_block_id, target.clone(), false, local_net.time_service(), storage.clone(), - async_std::task::block_on(registry.service_ref::>())?.clone(), + async_std::task::block_on( + registry.service_ref::>(), + )? + .clone(), target_node.clone(), local_ancestor_sender, DummyNetworkService::default(), @@ -1144,10 +1138,15 @@ fn sync_block_in_block_connection_service_mock( info!("checking branch in sync service is the same as target's branch"); assert_eq!(branch.current_header().id(), target.target_id.id()); - let block_connector_service = async_std::task::block_on(registry.service_ref::>())?.clone(); - let result = async_std::task::block_on(block_connector_service.send(CheckBlockConnectorHashValue { - head_hash: target.target_id.id(), - }))?; + let block_connector_service = async_std::task::block_on( + registry.service_ref::>(), + )? + .clone(); + let result = async_std::task::block_on(block_connector_service.send( + CheckBlockConnectorHashValue { + head_hash: target.target_id.id(), + }, + ))?; if result.is_ok() { break; } @@ -1165,7 +1164,7 @@ async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { let config = Arc::new(NodeConfig::random_for_test()); let (storage, chain_info, _) = StarcoinGenesis::init_storage_for_test(config.net()) .expect("init storage by genesis fail."); - + let target_node = Arc::new(SyncNodeMocker::new(config.net().clone(), 1, 0)?); let local_node = Arc::new(SyncNodeMocker::new_with_storage( config.net().clone(), @@ -1178,7 +1177,7 @@ async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { let (registry_sender, registry_receiver) = async_std::channel::unbounded(); - let _handle = timeout_join_handler::spawn(move|| { + let _handle = timeout_join_handler::spawn(move || { let system = System::with_tokio_rt(|| { tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -1198,19 +1197,29 @@ async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { registry .register::>() - .await.unwrap(); + .await + .unwrap(); registry_sender.send(registry).await.unwrap(); - }); - + system.run().unwrap(); }); let registry = registry_receiver.recv().await.unwrap(); - let target_node = sync_block_in_block_connection_service_mock(target_node, local_node.clone(), ®istry, 10)?; - _ = sync_block_in_block_connection_service_mock(target_node, local_node.clone(), ®istry, 20)?; + let target_node = sync_block_in_block_connection_service_mock( + target_node, + local_node.clone(), + ®istry, + 10, + )?; + _ = sync_block_in_block_connection_service_mock( + target_node, + local_node.clone(), + ®istry, + 20, + )?; Ok(()) } From 833cb23a05dad8aa64c9d53de3172af27c69e038 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Mon, 9 Oct 2023 10:12:15 +0800 Subject: [PATCH 03/17] add log for testing test_sync_block_apply_failed_but_connect_success --- sync/src/tasks/tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index bfa9b02e51..8666f73468 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -1177,6 +1177,8 @@ async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { let (registry_sender, registry_receiver) = async_std::channel::unbounded(); + info!("in test_sync_block_apply_failed_but_connect_success, start tokio runtime for main thread"); + let _handle = timeout_join_handler::spawn(move || { let system = System::with_tokio_rt(|| { tokio::runtime::Builder::new_multi_thread() From 19d4e1573ef2d2d477c5386fce3ca2921bea6850 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Mon, 9 Oct 2023 10:19:02 +0800 Subject: [PATCH 04/17] fix fmt --- sync/src/tasks/tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index 8666f73468..b9e731eab2 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -1177,7 +1177,9 @@ async fn test_sync_block_apply_failed_but_connect_success() -> Result<()> { let (registry_sender, registry_receiver) = async_std::channel::unbounded(); - info!("in test_sync_block_apply_failed_but_connect_success, start tokio runtime for main thread"); + info!( + "in test_sync_block_apply_failed_but_connect_success, start tokio runtime for main thread" + ); let _handle = timeout_join_handler::spawn(move || { let system = System::with_tokio_rt(|| { From f057caa8f7087d10980cf3c6211a045f0498f871 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Mon, 9 Oct 2023 17:38:25 +0800 Subject: [PATCH 05/17] sleep 12 sec for waiting for the fs drops the lock --- network/tests/network_node_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/tests/network_node_test.rs b/network/tests/network_node_test.rs index e17b9e94ae..c70ef5af26 100644 --- a/network/tests/network_node_test.rs +++ b/network/tests/network_node_test.rs @@ -35,7 +35,7 @@ fn test_reconnected_peers() -> anyhow::Result<()> { // stop node2, node1's peers is empty node2.stop()?; - thread::sleep(Duration::from_secs(3)); + thread::sleep(Duration::from_secs(12)); loop { let network_state = block_on(async { node1_network.network_state().await })?; debug!("network_state: {:?}", network_state); From 069de92ab48736af9d5b0c6a478fb6fbe1290455 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Fri, 13 Oct 2023 18:05:35 +0800 Subject: [PATCH 06/17] fix sync code --- Cargo.lock | 65 +- Cargo.toml | 5 +- benchmarks/src/chain.rs | 2 +- block-relayer/src/block_relayer.rs | 18 +- chain/Cargo.toml | 2 + chain/api/Cargo.toml | 1 + chain/api/src/chain.rs | 35 +- chain/api/src/errors.rs | 16 + chain/api/src/message.rs | 15 +- chain/api/src/service.rs | 71 +- chain/chain-notify/src/lib.rs | 2 +- chain/mock/src/mock_chain.rs | 6 +- chain/open-block/src/lib.rs | 4 + chain/service/Cargo.toml | 3 + chain/service/src/chain_service.rs | 170 ++++- chain/src/chain.rs | 293 +++++++- chain/src/verifier/mod.rs | 63 +- chain/tests/block_test_utils.rs | 2 +- chain/tests/test_block_chain.rs | 36 +- chain/tests/test_epoch_switch.rs | 18 +- chain/tests/test_opened_block.rs | 1 + chain/tests/test_txn_info_and_proof.rs | 4 +- cmd/db-exporter/src/main.rs | 26 +- cmd/peer-watcher/src/lib.rs | 11 +- cmd/peer-watcher/src/main.rs | 4 +- cmd/replay/src/main.rs | 14 +- cmd/starcoin/src/dev/gen_block_cmd.rs | 4 +- commons/accumulator/src/node.rs | 1 + commons/stream-task/src/collector.rs | 2 +- commons/time-service/src/lib.rs | 56 ++ config/src/genesis_config.rs | 1 + config/src/storage_config.rs | 18 + consensus/Cargo.toml | 9 + consensus/src/consensus.rs | 16 +- consensus/src/lib.rs | 8 + miner/src/create_block_template/mod.rs | 23 +- miner/src/lib.rs | 5 +- network-rpc/api/src/lib.rs | 18 +- network-rpc/src/rpc.rs | 40 +- network/api/src/messages.rs | 14 +- network/api/src/tests.rs | 30 +- network/src/network_p2p_handle.rs | 8 +- network/src/service.rs | 24 +- network/tests/network_service_test.rs | 15 +- network/types/src/peer_info.rs | 6 + node/Cargo.toml | 1 + node/src/node.rs | 76 +- rpc/server/src/module/chain_rpc.rs | 2 +- rpc/server/src/module/pubsub/tests.rs | 4 +- state/service/src/service.rs | 2 +- storage/src/accumulator/mod.rs | 19 +- storage/src/batch/mod.rs | 14 +- storage/src/block/mod.rs | 29 +- storage/src/cache_storage/mod.rs | 156 +++-- storage/src/chain_info/mod.rs | 16 + storage/src/db_storage/mod.rs | 54 +- storage/src/lib.rs | 212 +++++- storage/src/storage.rs | 24 +- storage/src/tests/mod.rs | 1 + storage/src/tests/test_storage.rs | 18 +- storage/src/upgrade.rs | 11 + sync/Cargo.toml | 2 + sync/api/src/lib.rs | 17 +- .../block_connector_service.rs | 37 +- sync/src/block_connector/mod.rs | 5 + .../src/block_connector/test_illegal_block.rs | 36 +- .../block_connector/test_write_block_chain.rs | 46 +- sync/src/block_connector/write_block_chain.rs | 648 ++++++++++++++++-- sync/src/sync.rs | 160 ++++- sync/src/tasks/block_sync_task.rs | 304 ++++++-- sync/src/tasks/inner_sync_task.rs | 18 +- sync/src/tasks/mock.rs | 23 +- sync/src/tasks/mod.rs | 102 ++- sync/src/tasks/tests.rs | 36 +- sync/src/verified_rpc_client.rs | 51 +- test-helper/src/chain.rs | 2 +- test-helper/src/network.rs | 20 +- test-helper/src/txn.rs | 2 +- txpool/src/test.rs | 5 +- types/src/block.rs | 96 ++- types/src/lib.rs | 3 + types/src/startup_info.rs | 89 ++- types/src/sync_status.rs | 32 +- types/src/system_events.rs | 10 +- vm/compiler/Cargo.toml | 2 +- vm/dev/Cargo.toml | 2 +- vm/gas-algebra-ext/Cargo.toml | 2 +- vm/move-coverage/Cargo.toml | 2 +- vm/move-explain/Cargo.toml | 2 +- vm/move-package-manager/Cargo.toml | 2 +- vm/move-package-manager/src/release.rs | 3 +- vm/move-prover/Cargo.toml | 2 +- vm/natives/Cargo.toml | 2 +- vm/resource-viewer/Cargo.toml | 2 +- vm/starcoin-gas/Cargo.toml | 2 +- .../Cargo.toml | 2 +- .../src/fork_chain.rs | 11 +- .../src/fork_state.rs | 11 +- .../src/lib.rs | 10 +- .../tests/cases/call_api_cmd.exp | 2 +- .../tests/cases/call_api_cmd_halley.exp | 2 +- vm/stdlib/Cargo.toml | 2 +- vm/stdlib/compiled/12/11-12/stdlib.blob | Bin 113341 -> 111158 bytes .../compiled/12/11-12/stdlib/008_Vector.mv | Bin 1256 -> 1240 bytes vm/stdlib/compiled/12/11-12/stdlib/010_ACL.mv | Bin 435 -> 465 bytes .../compiled/12/11-12/stdlib/012_Math.mv | Bin 688 -> 746 bytes .../compiled/12/11-12/stdlib/013_Option.mv | Bin 1051 -> 1136 bytes vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv | Bin 3074 -> 3135 bytes .../compiled/12/11-12/stdlib/021_VMConfig.mv | Bin 3967 -> 4003 bytes .../12/11-12/stdlib/036_Authenticator.mv | Bin 801 -> 873 bytes .../compiled/12/11-12/stdlib/037_Account.mv | Bin 6890 -> 6300 bytes .../compiled/12/11-12/stdlib/040_Ring.mv | Bin 1292 -> 1418 bytes .../12/11-12/stdlib/043_BlockReward.mv | Bin 1514 -> 1593 bytes .../12/11-12/stdlib/044_Collection.mv | Bin 814 -> 842 bytes .../12/11-12/stdlib/045_Collection2.mv | Bin 1860 -> 1924 bytes .../compiled/12/11-12/stdlib/046_Compare.mv | Bin 623 -> 682 bytes .../12/11-12/stdlib/051_EVMAddress.mv | Bin 400 -> 476 bytes vm/stdlib/compiled/12/stdlib/008_Vector.mv | Bin 1256 -> 1240 bytes vm/stdlib/compiled/12/stdlib/010_ACL.mv | Bin 435 -> 465 bytes vm/stdlib/compiled/12/stdlib/012_Math.mv | Bin 688 -> 746 bytes vm/stdlib/compiled/12/stdlib/013_Option.mv | Bin 1051 -> 1136 bytes vm/stdlib/compiled/12/stdlib/014_BCS.mv | Bin 3074 -> 3135 bytes vm/stdlib/compiled/12/stdlib/021_VMConfig.mv | Bin 3967 -> 4003 bytes .../compiled/12/stdlib/036_Authenticator.mv | Bin 801 -> 873 bytes vm/stdlib/compiled/12/stdlib/037_Account.mv | Bin 6890 -> 6300 bytes vm/stdlib/compiled/12/stdlib/040_Ring.mv | Bin 1292 -> 1418 bytes .../compiled/12/stdlib/043_BlockReward.mv | Bin 1514 -> 1593 bytes .../compiled/12/stdlib/044_Collection.mv | Bin 814 -> 842 bytes .../compiled/12/stdlib/045_Collection2.mv | Bin 1860 -> 1924 bytes vm/stdlib/compiled/12/stdlib/046_Compare.mv | Bin 623 -> 682 bytes .../compiled/12/stdlib/051_EVMAddress.mv | Bin 400 -> 476 bytes .../error_descriptions.errmap | Bin 10372 -> 9962 bytes .../compiled/latest/stdlib/008_Vector.mv | Bin 1256 -> 1240 bytes vm/stdlib/compiled/latest/stdlib/010_ACL.mv | Bin 435 -> 465 bytes vm/stdlib/compiled/latest/stdlib/012_Math.mv | Bin 688 -> 746 bytes .../compiled/latest/stdlib/013_Option.mv | Bin 1051 -> 1136 bytes vm/stdlib/compiled/latest/stdlib/014_BCS.mv | Bin 3074 -> 3135 bytes .../compiled/latest/stdlib/021_VMConfig.mv | Bin 3967 -> 4003 bytes .../latest/stdlib/036_Authenticator.mv | Bin 801 -> 873 bytes .../compiled/latest/stdlib/037_Account.mv | Bin 6890 -> 6300 bytes vm/stdlib/compiled/latest/stdlib/040_Ring.mv | Bin 1292 -> 1418 bytes .../compiled/latest/stdlib/043_BlockReward.mv | Bin 1514 -> 1593 bytes .../compiled/latest/stdlib/044_Collection.mv | Bin 814 -> 842 bytes .../compiled/latest/stdlib/045_Collection2.mv | Bin 1860 -> 1924 bytes .../compiled/latest/stdlib/046_Compare.mv | Bin 623 -> 682 bytes .../compiled/latest/stdlib/051_EVMAddress.mv | Bin 400 -> 476 bytes vm/transaction-builder-generator/Cargo.toml | 2 +- vm/transaction-builder/Cargo.toml | 3 +- vm/transaction-builder/src/lib.rs | 327 +++++---- vm/types/Cargo.toml | 2 +- .../src/account_config/constants/addresses.rs | 36 +- vm/types/src/lib.rs | 1 + vm/types/src/on_chain_config/gas_schedule.rs | 2 + vm/types/src/on_chain_config/mod.rs | 1 + vm/types/src/proptest_types.rs | 2 +- vm/types/src/state_store/state_key.rs | 3 + vm/types/src/state_store/table.rs | 36 +- vm/types/src/transaction/mod.rs | 35 +- vm/vm-runtime/Cargo.toml | 25 +- vm/vm-runtime/src/data_cache.rs | 1 - vm/vm-runtime/src/lib.rs | 34 +- vm/vm-runtime/src/move_vm_ext/session.rs | 21 +- vm/vm-runtime/src/starcoin_vm.rs | 223 ++---- vm/vm-status-translator/Cargo.toml | 2 +- 164 files changed, 3274 insertions(+), 1113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28307fda11..1196f516e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,6 +377,16 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote 1.0.28", + "syn 1.0.107", +] + [[package]] name = "async-channel" version = "1.8.0" @@ -428,6 +438,7 @@ dependencies = [ "blocking", "futures-lite", "once_cell", + "tokio", ] [[package]] @@ -466,6 +477,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ + "async-attributes", "async-channel", "async-global-executor", "async-io", @@ -5246,7 +5258,7 @@ dependencies = [ [[package]] name = "move-coverage" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs", @@ -5440,7 +5452,7 @@ dependencies = [ [[package]] name = "move-package-manager" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs", @@ -9270,6 +9282,7 @@ dependencies = [ "starcoin-executor", "starcoin-genesis", "starcoin-logger", + "starcoin-network-rpc-api", "starcoin-open-block", "starcoin-resource-viewer", "starcoin-service-registry", @@ -9300,6 +9313,7 @@ dependencies = [ "serde 1.0.152", "starcoin-accumulator", "starcoin-crypto", + "starcoin-network-rpc-api", "starcoin-service-registry", "starcoin-state-api", "starcoin-statedb", @@ -9361,11 +9375,14 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "serde 1.0.152", + "starcoin-accumulator", "starcoin-chain", "starcoin-chain-api", "starcoin-config", + "starcoin-consensus", "starcoin-crypto", "starcoin-logger", + "starcoin-network-rpc-api", "starcoin-service-registry", "starcoin-state-api", "starcoin-storage", @@ -9481,25 +9498,34 @@ name = "starcoin-consensus" version = "1.13.7" dependencies = [ "anyhow", + "bcs-ext", + "bincode", "byteorder", "cryptonight-rs", "futures 0.3.26", "hex", + "itertools", "once_cell", + "parking_lot 0.12.1", "proptest", "proptest-derive", "rand 0.8.5", "rand_core 0.6.4", + "rocksdb", "rust-argon2", + "serde 1.0.152", "sha3", "starcoin-chain-api", + "starcoin-config", "starcoin-crypto", "starcoin-logger", "starcoin-state-api", + "starcoin-storage", "starcoin-time-service", "starcoin-types", "starcoin-vm-types", "stest", + "tempfile", "thiserror", ] @@ -9562,7 +9588,7 @@ dependencies = [ [[package]] name = "starcoin-dev" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs-ext", @@ -9681,7 +9707,7 @@ dependencies = [ [[package]] name = "starcoin-gas" -version = "1.13.7" +version = "1.13.5" dependencies = [ "clap 3.2.23", "move-binary-format", @@ -9696,7 +9722,7 @@ dependencies = [ [[package]] name = "starcoin-gas-algebra-ext" -version = "1.13.7" +version = "1.13.5" dependencies = [ "move-binary-format", "move-core-types", @@ -9951,7 +9977,7 @@ dependencies = [ [[package]] name = "starcoin-move-compiler" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "move-binary-format", @@ -9970,7 +9996,7 @@ dependencies = [ [[package]] name = "starcoin-move-explain" -version = "1.13.7" +version = "1.13.5" dependencies = [ "bcs-ext", "clap 3.2.23", @@ -9980,7 +10006,7 @@ dependencies = [ [[package]] name = "starcoin-move-prover" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "atty", @@ -10022,7 +10048,7 @@ dependencies = [ [[package]] name = "starcoin-natives" -version = "1.13.7" +version = "1.13.5" dependencies = [ "arrayref", "hex", @@ -10178,6 +10204,7 @@ dependencies = [ "serde_json", "starcoin-account-api", "starcoin-account-service", + "starcoin-accumulator", "starcoin-block-relayer", "starcoin-chain-notify", "starcoin-chain-service", @@ -10325,7 +10352,7 @@ dependencies = [ [[package]] name = "starcoin-resource-viewer" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "hex", @@ -10800,14 +10827,13 @@ dependencies = [ [[package]] name = "starcoin-transaction-builder" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs-ext", "starcoin-config", "starcoin-crypto", "starcoin-logger", - "starcoin-types", "starcoin-vm-types", "stdlib", "stest", @@ -10815,7 +10841,7 @@ dependencies = [ [[package]] name = "starcoin-transactional-test-harness" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "async-trait", @@ -10998,7 +11024,7 @@ dependencies = [ [[package]] name = "starcoin-vm-runtime" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs-ext", @@ -11006,12 +11032,10 @@ dependencies = [ "move-stdlib", "move-table-extension", "move-vm-runtime", - "num_cpus", "num_enum", "once_cell", "rand 0.8.5", "rand_core 0.6.4", - "rayon", "serde 1.0.152", "starcoin-config", "starcoin-crypto", @@ -11020,7 +11044,6 @@ dependencies = [ "starcoin-logger", "starcoin-metrics", "starcoin-natives", - "starcoin-parallel-executor", "starcoin-types", "starcoin-vm-types", "stdlib", @@ -11029,7 +11052,7 @@ dependencies = [ [[package]] name = "starcoin-vm-types" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs-ext", @@ -11068,7 +11091,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stdlib" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs-ext", @@ -12034,7 +12057,7 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "transaction-builder-generator" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "bcs", @@ -12479,7 +12502,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vm-status-translator" -version = "1.13.7" +version = "1.13.5" dependencies = [ "anyhow", "schemars", diff --git a/Cargo.toml b/Cargo.toml index d8cee9cb41..fc7a208a3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -246,7 +246,7 @@ api-limiter = { path = "commons/api-limiter" } arc-swap = "1.5.1" arrayref = "0.3" ascii = "1.0.0" -async-std = "1.12" +async-std = { version = "1.12", features = ["attributes", "tokio1"] } async-trait = "0.1.53" asynchronous-codec = "0.5" atomic-counter = "1.0.1" @@ -257,6 +257,9 @@ bcs-ext = { path = "commons/bcs_ext" } bech32 = "0.9" bencher = "0.1.5" bitflags = "1.3.2" +faster-hex = "0.6" +indexmap = "1.9.1" +bincode = { version = "1", default-features = false } bs58 = "0.3.1" byteorder = "1.3.4" bytes = "1" diff --git a/benchmarks/src/chain.rs b/benchmarks/src/chain.rs index ede8471734..e46462471c 100644 --- a/benchmarks/src/chain.rs +++ b/benchmarks/src/chain.rs @@ -71,7 +71,7 @@ impl ChainBencher { let block = ConsensusStrategy::Dummy .create_block(block_template, self.net.time_service().as_ref()) .unwrap(); - self.chain.write().apply(block).unwrap(); + self.chain.write().apply(block, None, &mut None).unwrap(); } } diff --git a/block-relayer/src/block_relayer.rs b/block-relayer/src/block_relayer.rs index d8978076ae..88767d572c 100644 --- a/block-relayer/src/block_relayer.rs +++ b/block-relayer/src/block_relayer.rs @@ -78,14 +78,18 @@ impl BlockRelayer { &self, network: NetworkServiceRef, executed_block: Arc, + tips_header: Option>, ) { if !self.is_nearly_synced() { debug!("[block-relay] Ignore NewHeadBlock event because the node has not been synchronized yet."); return; } let compact_block = executed_block.block().clone().into(); - let compact_block_msg = - CompactBlockMessage::new(compact_block, executed_block.block_info.clone()); + let compact_block_msg = CompactBlockMessage::new( + compact_block, + executed_block.block_info.clone(), + tips_header, + ); network.broadcast(NotificationMessage::CompactBlock(Box::new( compact_block_msg, ))); @@ -240,7 +244,11 @@ impl BlockRelayer { ) .await?; - block_connector_service.notify(PeerNewBlock::new(peer_id, block))?; + block_connector_service.notify(PeerNewBlock::new( + peer_id, + block, + compact_block_msg.message.tips_header, + ))?; } Ok(()) }; @@ -288,7 +296,7 @@ impl EventHandler for BlockRelayer { return; } }; - self.broadcast_compact_block(network, event.0); + self.broadcast_compact_block(network, event.0, event.1); } } @@ -305,7 +313,7 @@ impl EventHandler for BlockRelayer { return; } }; - self.broadcast_compact_block(network, event.0); + self.broadcast_compact_block(network, event.0, event.1); } } diff --git a/chain/Cargo.toml b/chain/Cargo.toml index 44d63cc92c..8dac82c310 100644 --- a/chain/Cargo.toml +++ b/chain/Cargo.toml @@ -23,6 +23,7 @@ starcoin-types = { package = "starcoin-types", workspace = true } starcoin-vm-types = { workspace = true } starcoin-storage = { workspace = true } thiserror = { workspace = true } +starcoin-network-rpc-api = { workspace = true } [dev-dependencies] proptest = { workspace = true } @@ -39,6 +40,7 @@ stdlib = { workspace = true } stest = { workspace = true } test-helper = { workspace = true } tokio = { features = ["full"], workspace = true } +starcoin-network-rpc-api = { workspace = true } [features] default = [] diff --git a/chain/api/Cargo.toml b/chain/api/Cargo.toml index 6b6b855e79..47235e7bb9 100644 --- a/chain/api/Cargo.toml +++ b/chain/api/Cargo.toml @@ -15,6 +15,7 @@ starcoin-time-service = { workspace = true } starcoin-types = { workspace = true } starcoin-vm-types = { workspace = true } thiserror = { workspace = true } +starcoin-network-rpc-api = { workspace = true } [dev-dependencies] diff --git a/chain/api/src/chain.rs b/chain/api/src/chain.rs index 93884610e2..4249859727 100644 --- a/chain/api/src/chain.rs +++ b/chain/api/src/chain.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2 use anyhow::Result; +use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_crypto::HashValue; use starcoin_state_api::ChainStateReader; use starcoin_statedb::ChainStateDB; @@ -80,7 +81,11 @@ pub trait ChainReader { /// Verify block header and body, base current chain, but do not verify it execute state. fn verify(&self, block: Block) -> Result; /// Execute block and verify it execute state, and save result base current chain, but do not change current chain. - fn execute(&self, block: VerifiedBlock) -> Result; + fn execute( + &self, + block: VerifiedBlock, + dag_block_parents: Option, + ) -> Result; /// Get chain transaction infos fn get_transaction_infos( &self, @@ -100,17 +105,41 @@ pub trait ChainReader { event_index: Option, access_path: Option, ) -> Result>; + + /// get the current tips hash value + fn current_tips_hash(&self) -> Option; } pub trait ChainWriter { fn can_connect(&self, executed_block: &ExecutedBlock) -> bool; /// Connect a executed block to current chain. - fn connect(&mut self, executed_block: ExecutedBlock) -> Result; + fn connect( + &mut self, + executed_block: ExecutedBlock, + next_tips: &mut Option>, + ) -> Result; /// Verify, Execute and Connect block to current chain. - fn apply(&mut self, block: Block) -> Result; + fn apply( + &mut self, + block: Block, + dag_block_next_parent: Option, + next_tips: &mut Option>, + ) -> Result; fn chain_state(&mut self) -> &ChainStateDB; + + /// Get the dag accumulator info + fn get_current_dag_accumulator_info(&self) -> Result; + + /// Fork the accumulator + fn fork_dag_accumulator(&mut self, accumulator_info: AccumulatorInfo) -> Result<()>; + + /// Append the dag accumulator leaf + fn append_dag_accumulator_leaf( + &mut self, + tips: Vec, + ) -> Result<(HashValue, AccumulatorInfo)>; } /// `Chain` is a trait that defines a single Chain. diff --git a/chain/api/src/errors.rs b/chain/api/src/errors.rs index 777cb19e7c..0fccef901c 100644 --- a/chain/api/src/errors.rs +++ b/chain/api/src/errors.rs @@ -63,6 +63,10 @@ pub enum ConnectBlockError { VerifyBlockFailed(VerifyBlockField, Error), #[error("Barnard hard fork block: {:?} ", .0.header())] BarnardHardFork(Box), + #[error("dag block before time window: {:?} ", .0.header())] + DagBlockBeforeTimeWindow(Box), + #[error("dag block after time window: {:?} ", .0.header())] + DagBlockAfterTimeWindow(Box), } impl ConnectBlockError { @@ -74,6 +78,10 @@ impl ConnectBlockError { ReputationChange::new_fatal("VerifyBlockFailed"); pub const REP_BARNARD_HARD_FORK: ReputationChange = ReputationChange::new_fatal("BarnardHardFork"); + pub const REP_BLOCK_BEFORE_TIME_WINDOW: ReputationChange = + ReputationChange::new_fatal("DagBlockBeforeTimeWindow"); + pub const REP_BLOCK_AFTER_TIME_WINDOW: ReputationChange = + ReputationChange::new_fatal("DagBlockAfterTimeWindow"); pub fn reason(&self) -> &str { match self { @@ -81,6 +89,8 @@ impl ConnectBlockError { ConnectBlockError::ParentNotExist(_) => "ParentNotExist", ConnectBlockError::VerifyBlockFailed(_, _) => "VerifyBlockFailed", ConnectBlockError::BarnardHardFork(_) => "BarnardHardFork", + ConnectBlockError::DagBlockBeforeTimeWindow(_) => "DagBlockBeforeTimeWindow", + ConnectBlockError::DagBlockAfterTimeWindow(_) => "DagBlockAfterTimeWindow", } } @@ -92,6 +102,12 @@ impl ConnectBlockError { ConnectBlockError::REP_VERIFY_BLOCK_FAILED } ConnectBlockError::BarnardHardFork(_) => ConnectBlockError::REP_BARNARD_HARD_FORK, + ConnectBlockError::DagBlockBeforeTimeWindow(_) => { + ConnectBlockError::REP_BLOCK_BEFORE_TIME_WINDOW + } + ConnectBlockError::DagBlockAfterTimeWindow(_) => { + ConnectBlockError::REP_BLOCK_AFTER_TIME_WINDOW + } } } } diff --git a/chain/api/src/message.rs b/chain/api/src/message.rs index d4144fe9a0..d023481215 100644 --- a/chain/api/src/message.rs +++ b/chain/api/src/message.rs @@ -4,6 +4,9 @@ use crate::TransactionInfoWithProof; use anyhow::Result; use starcoin_crypto::HashValue; +use starcoin_network_rpc_api::dag_protocol::{ + TargetDagAccumulatorLeaf, TargetDagAccumulatorLeafDetail, +}; use starcoin_service_registry::ServiceRequest; use starcoin_types::transaction::RichTransactionInfo; use starcoin_types::{ @@ -60,6 +63,14 @@ pub enum ChainRequest { access_path: Option, }, GetBlockInfos(Vec), + GetDagAccumulatorLeaves { + start_index: u64, + batch_size: u64, + }, + GetTargetDagAccumulatorLeafDetail { + leaf_index: u64, + batch_size: u64, + }, } impl ServiceRequest for ChainRequest { @@ -79,7 +90,7 @@ pub enum ChainResponse { Transaction(Box), TransactionOption(Option>), BlockVec(Vec), - BlockOptionVec(Vec>), + BlockOptionVec(Vec>, Option)>>), BlockHeaderVec(Vec>), TransactionInfos(Vec), TransactionInfo(Option), @@ -88,4 +99,6 @@ pub enum ChainResponse { HashVec(Vec), TransactionProof(Box>), BlockInfoVec(Box>>), + TargetDagAccumulatorLeaf(Vec), + TargetDagAccumulatorLeafDetail(Vec), } diff --git a/chain/api/src/service.rs b/chain/api/src/service.rs index 8ba6adce0e..2610b8862c 100644 --- a/chain/api/src/service.rs +++ b/chain/api/src/service.rs @@ -5,6 +5,10 @@ use crate::message::{ChainRequest, ChainResponse}; use crate::TransactionInfoWithProof; use anyhow::{bail, Result}; use starcoin_crypto::HashValue; +use starcoin_network_rpc_api::dag_protocol::{ + self, GetDagAccumulatorLeaves, GetTargetDagAccumulatorLeafDetail, TargetDagAccumulatorLeaf, + TargetDagAccumulatorLeafDetail, +}; use starcoin_service_registry::{ActorService, ServiceHandler, ServiceRef}; use starcoin_types::contract_event::{ContractEvent, ContractEventInfo}; use starcoin_types::filter::Filter; @@ -20,7 +24,10 @@ use starcoin_vm_types::access_path::AccessPath; pub trait ReadableChainService { fn get_header_by_hash(&self, hash: HashValue) -> Result>; fn get_block_by_hash(&self, hash: HashValue) -> Result>; - fn get_blocks(&self, ids: Vec) -> Result>>; + fn get_blocks( + &self, + ids: Vec, + ) -> Result>, Option)>>>; fn get_headers(&self, ids: Vec) -> Result>>; fn get_block_info_by_hash(&self, hash: HashValue) -> Result>; fn get_transaction(&self, hash: HashValue) -> Result>; @@ -72,11 +79,19 @@ pub trait ReadableChainService { ) -> Result>; fn get_block_infos(&self, ids: Vec) -> Result>>; + fn get_dag_accumulator_leaves( + &self, + req: GetDagAccumulatorLeaves, + ) -> anyhow::Result>; + fn get_target_dag_accumulator_leaf_detail( + &self, + req: GetTargetDagAccumulatorLeafDetail, + ) -> anyhow::Result>; } /// Writeable block chain service trait pub trait WriteableChainService: Send + Sync { - fn try_connect(&mut self, block: Block) -> Result<()>; + fn try_connect(&mut self, block: Block, tips_header: Option>) -> Result<()>; } #[async_trait::async_trait] @@ -85,7 +100,10 @@ pub trait ChainAsyncService: { async fn get_header_by_hash(&self, hash: &HashValue) -> Result>; async fn get_block_by_hash(&self, hash: HashValue) -> Result>; - async fn get_blocks(&self, hashes: Vec) -> Result>>; + async fn get_blocks( + &self, + hashes: Vec, + ) -> Result>, Option)>>>; async fn get_headers(&self, hashes: Vec) -> Result>>; async fn get_block_info_by_hash(&self, hash: &HashValue) -> Result>; async fn get_block_info_by_number(&self, number: u64) -> Result>; @@ -139,6 +157,14 @@ pub trait ChainAsyncService: ) -> Result>; async fn get_block_infos(&self, hashes: Vec) -> Result>>; + async fn get_dag_accumulator_leaves( + &self, + req: dag_protocol::GetDagAccumulatorLeaves, + ) -> Result>; + async fn get_dag_accumulator_leaves_detail( + &self, + req: dag_protocol::GetTargetDagAccumulatorLeafDetail, + ) -> Result>>; } #[async_trait::async_trait] @@ -170,7 +196,10 @@ where } } - async fn get_blocks(&self, hashes: Vec) -> Result>> { + async fn get_blocks( + &self, + hashes: Vec, + ) -> Result>, Option)>>> { if let ChainResponse::BlockOptionVec(blocks) = self.send(ChainRequest::GetBlocks(hashes)).await?? { @@ -180,6 +209,40 @@ where } } + async fn get_dag_accumulator_leaves( + &self, + req: dag_protocol::GetDagAccumulatorLeaves, + ) -> Result> { + if let ChainResponse::TargetDagAccumulatorLeaf(leaves) = self + .send(ChainRequest::GetDagAccumulatorLeaves { + start_index: req.accumulator_leaf_index, + batch_size: req.batch_size, + }) + .await?? + { + Ok(leaves) + } else { + bail!("get_dag_accumulator_leaves response type error.") + } + } + + async fn get_dag_accumulator_leaves_detail( + &self, + req: dag_protocol::GetTargetDagAccumulatorLeafDetail, + ) -> Result>> { + if let ChainResponse::TargetDagAccumulatorLeafDetail(details) = self + .send(ChainRequest::GetTargetDagAccumulatorLeafDetail { + leaf_index: req.leaf_index, + batch_size: req.batch_size, + }) + .await?? + { + Ok(Some(details)) + } else { + Ok(None) + } + } + async fn get_headers(&self, ids: Vec) -> Result>> { if let ChainResponse::BlockHeaderVec(headers) = self.send(ChainRequest::GetHeaders(ids)).await?? diff --git a/chain/chain-notify/src/lib.rs b/chain/chain-notify/src/lib.rs index 60c1985dbe..021a19f65a 100644 --- a/chain/chain-notify/src/lib.rs +++ b/chain/chain-notify/src/lib.rs @@ -52,7 +52,7 @@ impl EventHandler for ChainNotifyHandlerService { item: NewHeadBlock, ctx: &mut ServiceContext, ) { - let NewHeadBlock(block_detail) = item; + let NewHeadBlock(block_detail, _dag_parents, _next_tips) = item; let block = block_detail.block(); // notify header. self.notify_new_block(block, ctx); diff --git a/chain/mock/src/mock_chain.rs b/chain/mock/src/mock_chain.rs index 403cd09611..0cc234cdf4 100644 --- a/chain/mock/src/mock_chain.rs +++ b/chain/mock/src/mock_chain.rs @@ -120,15 +120,15 @@ impl MockChain { .create_block(template, self.net.time_service().as_ref()) } - pub fn apply(&mut self, block: Block) -> Result<()> { - self.head.apply(block)?; + pub fn apply(&mut self, block: Block, dag_parent: Option) -> Result<()> { + self.head.apply(block, dag_parent, &mut None)?; Ok(()) } pub fn produce_and_apply(&mut self) -> Result { let block = self.produce()?; let header = block.header().clone(); - self.apply(block)?; + self.apply(block, None)?; Ok(header) } diff --git a/chain/open-block/src/lib.rs b/chain/open-block/src/lib.rs index 7df7510ecd..52fb3800ab 100644 --- a/chain/open-block/src/lib.rs +++ b/chain/open-block/src/lib.rs @@ -39,6 +39,7 @@ pub struct OpenedBlock { difficulty: U256, strategy: ConsensusStrategy, vm_metrics: Option, + tips_header: Option>, } impl OpenedBlock { @@ -52,6 +53,7 @@ impl OpenedBlock { difficulty: U256, strategy: ConsensusStrategy, vm_metrics: Option, + tips_header: Option>, ) -> Result { let previous_block_id = previous_header.id(); let block_info = storage @@ -90,6 +92,7 @@ impl OpenedBlock { difficulty, strategy, vm_metrics, + tips_header, }; opened_block.initialize()?; Ok(opened_block) @@ -284,6 +287,7 @@ impl OpenedBlock { self.difficulty, self.strategy, self.block_meta, + self.tips_header, ); Ok(block_template) } diff --git a/chain/service/Cargo.toml b/chain/service/Cargo.toml index e375203781..e1dbd7f95d 100644 --- a/chain/service/Cargo.toml +++ b/chain/service/Cargo.toml @@ -18,6 +18,9 @@ starcoin-vm-runtime = { workspace = true } starcoin-vm-types = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } +starcoin-network-rpc-api = { workspace = true } +starcoin-consensus = { workspace = true } +starcoin-accumulator = { package = "starcoin-accumulator", workspace = true } [dev-dependencies] stest = { workspace = true } diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index f7b32799d1..e93d41e2d5 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -1,15 +1,22 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::{format_err, Error, Result}; +use anyhow::{bail, format_err, Error, Result}; +use starcoin_accumulator::node::AccumulatorStoreType; +use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::BlockChain; use starcoin_chain_api::message::{ChainRequest, ChainResponse}; use starcoin_chain_api::{ ChainReader, ChainWriter, ReadableChainService, TransactionInfoWithProof, }; use starcoin_config::NodeConfig; +use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; +use starcoin_network_rpc_api::dag_protocol::{ + GetDagAccumulatorLeaves, GetTargetDagAccumulatorLeafDetail, TargetDagAccumulatorLeaf, + TargetDagAccumulatorLeafDetail, +}; use starcoin_service_registry::{ ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, }; @@ -27,7 +34,7 @@ use starcoin_types::{ }; use starcoin_vm_runtime::metrics::VMMetrics; use starcoin_vm_types::access_path::AccessPath; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; /// A Chain reader service to provider Reader API. pub struct ChainReaderService { @@ -39,10 +46,17 @@ impl ChainReaderService { config: Arc, startup_info: StartupInfo, storage: Arc, + dag: Arc>, vm_metrics: Option, ) -> Result { Ok(Self { - inner: ChainReaderServiceInner::new(config, startup_info, storage, vm_metrics)?, + inner: ChainReaderServiceInner::new( + config.clone(), + startup_info, + storage.clone(), + dag.clone(), + vm_metrics.clone(), + )?, }) } } @@ -55,7 +69,10 @@ impl ServiceFactory for ChainReaderService { .get_startup_info()? .ok_or_else(|| format_err!("StartupInfo should exist at service init."))?; let vm_metrics = ctx.get_shared_opt::()?; - Self::new(config, startup_info, storage, vm_metrics) + let dag = ctx + .get_shared_opt::>>()? + .expect("dag should be initialized at service init"); + Self::new(config, startup_info, storage, dag.clone(), vm_metrics) } } @@ -75,7 +92,15 @@ impl EventHandler for ChainReaderService { fn handle_event(&mut self, event: NewHeadBlock, _ctx: &mut ServiceContext) { let new_head = event.0.block().header(); if let Err(e) = if self.inner.get_main().can_connect(event.0.as_ref()) { - self.inner.update_chain_head(event.0.as_ref().clone()) + let mut next_tips = event.2.clone(); + + match self + .inner + .update_chain_head(event.0.as_ref().clone(), &mut next_tips) + { + Ok(_) => self.inner.update_dag_accumulator(new_head.id()), + Err(e) => Err(e), + } } else { self.inner.switch_main(new_head.id()) } { @@ -232,6 +257,27 @@ impl ServiceHandler for ChainReaderService { ChainRequest::GetBlockInfos(ids) => Ok(ChainResponse::BlockInfoVec(Box::new( self.inner.get_block_infos(ids)?, ))), + ChainRequest::GetDagAccumulatorLeaves { + start_index, + batch_size, + } => Ok(ChainResponse::TargetDagAccumulatorLeaf( + self.inner + .get_dag_accumulator_leaves(GetDagAccumulatorLeaves { + accumulator_leaf_index: start_index, + batch_size, + })?, + )), + ChainRequest::GetTargetDagAccumulatorLeafDetail { + leaf_index, + batch_size, + } => Ok(ChainResponse::TargetDagAccumulatorLeafDetail( + self.inner.get_target_dag_accumulator_leaf_detail( + GetTargetDagAccumulatorLeafDetail { + leaf_index, + batch_size, + }, + )?, + )), } } } @@ -242,6 +288,7 @@ pub struct ChainReaderServiceInner { main: BlockChain, storage: Arc, vm_metrics: Option, + dag: Arc>, } impl ChainReaderServiceInner { @@ -249,6 +296,7 @@ impl ChainReaderServiceInner { config: Arc, startup_info: StartupInfo, storage: Arc, + dag: Arc>, vm_metrics: Option, ) -> Result { let net = config.net(); @@ -263,6 +311,7 @@ impl ChainReaderServiceInner { startup_info, main, storage, + dag: dag.clone(), vm_metrics, }) } @@ -271,8 +320,12 @@ impl ChainReaderServiceInner { &self.main } - pub fn update_chain_head(&mut self, block: ExecutedBlock) -> Result<()> { - self.main.connect(block)?; + pub fn update_chain_head( + &mut self, + block: ExecutedBlock, + next_tips: &mut Option>, + ) -> Result<()> { + self.main.connect(block, next_tips)?; Ok(()) } @@ -286,6 +339,10 @@ impl ChainReaderServiceInner { )?; Ok(()) } + + pub fn update_dag_accumulator(&mut self, head_id: HashValue) -> Result<()> { + self.main.update_dag_accumulator(head_id) + } } impl ReadableChainService for ChainReaderServiceInner { @@ -297,15 +354,54 @@ impl ReadableChainService for ChainReaderServiceInner { self.storage.get_block_by_hash(hash) } - fn get_blocks(&self, ids: Vec) -> Result>> { - self.storage.get_blocks(ids) + fn get_blocks( + &self, + ids: Vec, + ) -> Result>, Option)>>> { + let blocks = self.storage.get_blocks(ids)?; + Ok(blocks + .into_iter() + .map(|block| { + if let Some(block) = block { + let parents = match self + .dag + .lock() + .expect("failed to lock dag") + .get_parents(block.id()) + { + Ok(parents) => parents, + Err(_) => panic!("failed to get parents of block {}", block.id()), + }; + let transaction_parent = match self.storage.get_block_info(block.id()) { + Ok(block_info) => { + if let Some(block_info) = &block_info { + let block_accumulator = MerkleAccumulator::new_with_info( + block_info.block_accumulator_info.clone(), + self.storage + .get_accumulator_store(AccumulatorStoreType::Block), + ); + block_accumulator + .get_leaf(block_info.block_accumulator_info.num_leaves - 2) + .expect("block should have transction header") + } else { + None + } + } + Err(_) => todo!(), + }; + Some((block, Some(parents), transaction_parent)) + } else { + None + } + }) + .collect()) } fn get_headers(&self, ids: Vec) -> Result>> { Ok(self .get_blocks(ids)? .into_iter() - .map(|block| block.map(|b| b.header)) + .map(|block| block.map(|b| b.0.header)) .collect()) } @@ -416,6 +512,60 @@ impl ReadableChainService for ChainReaderServiceInner { fn get_block_infos(&self, ids: Vec) -> Result>> { self.storage.get_block_infos(ids) } + + fn get_dag_accumulator_leaves( + &self, + req: GetDagAccumulatorLeaves, + ) -> anyhow::Result> { + match self + .main + .get_dag_leaves(req.accumulator_leaf_index, true, req.batch_size) + { + Ok(leaves) => Ok(leaves + .into_iter() + .enumerate() + .map( + |(index, leaf)| match self.main.get_dag_accumulator_snapshot(leaf) { + Ok(snapshot) => TargetDagAccumulatorLeaf { + accumulator_root: snapshot.accumulator_info.accumulator_root, + leaf_index: req.accumulator_leaf_index.saturating_sub(index as u64), + }, + Err(error) => { + panic!( + "error occured when query the accumulator snapshot: {}", + error.to_string() + ); + } + }, + ) + .collect()), + Err(error) => { + bail!( + "an error occured when getting the leaves of the accumulator, {}", + error.to_string() + ); + } + } + } + + fn get_target_dag_accumulator_leaf_detail( + &self, + req: GetTargetDagAccumulatorLeafDetail, + ) -> anyhow::Result> { + let end_index = std::cmp::min( + req.leaf_index + req.batch_size - 1, + self.main.get_dag_current_leaf_number()? - 1, + ); + let mut details = [].to_vec(); + for index in req.leaf_index..=end_index { + let snapshot = self.main.get_dag_accumulator_snapshot_by_index(index)?; + details.push(TargetDagAccumulatorLeafDetail { + accumulator_root: snapshot.accumulator_info.accumulator_root, + tips: snapshot.child_hashes, + }); + } + Ok(details) + } } #[cfg(test)] diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 1c7825d4c7..ddc2cb4b6b 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::verifier::{BlockVerifier, FullVerifier}; -use anyhow::{bail, ensure, format_err, Result}; +use anyhow::{anyhow, bail, ensure, format_err, Ok, Result}; +use bcs_ext::BCSCodec; use sp_utils::stop_watch::{watch, CHAIN_WATCH_NAME}; use starcoin_accumulator::inmemory::InMemoryAccumulator; use starcoin_accumulator::{ @@ -20,6 +21,8 @@ use starcoin_logger::prelude::*; use starcoin_open_block::OpenedBlock; use starcoin_state_api::{AccountStateReader, ChainStateReader, ChainStateWriter}; use starcoin_statedb::ChainStateDB; +use starcoin_storage::flexi_dag::SyncFlexiDagSnapshot; +use starcoin_storage::storage::CodecKVStore; use starcoin_storage::Store; use starcoin_time_service::TimeService; use starcoin_types::block::BlockIdAndNumber; @@ -60,6 +63,7 @@ pub struct BlockChain { uncles: HashMap, epoch: Epoch, vm_metrics: Option, + dag_accumulator: Option, } impl BlockChain { @@ -94,7 +98,20 @@ impl BlockChain { let genesis = storage .get_genesis()? .ok_or_else(|| format_err!("Can not find genesis hash in storage."))?; + let head_id = head_block.id(); watch(CHAIN_WATCH_NAME, "n1253"); + let dag_accumulator = match storage.get_dag_accumulator_info(head_id)? { + Some(accmulator_info) => Some(info_2_accumulator( + accmulator_info, + AccumulatorStoreType::SyncDag, + storage.as_ref(), + )), + None => None, + }; + let dag_snapshot_tips = storage + .get_accumulator_snapshot_storage() + .get(head_id)? + .map(|snapshot| snapshot.child_hashes); let mut chain = Self { genesis_hash: genesis, time_service, @@ -109,14 +126,15 @@ impl BlockChain { storage.as_ref(), ), status: ChainStatusWithBlock { - status: ChainStatus::new(head_block.header.clone(), block_info), + status: ChainStatus::new(head_block.header.clone(), block_info, dag_snapshot_tips), head: head_block, }, statedb: chain_state, - storage, + storage: storage.clone(), uncles: HashMap::new(), epoch, vm_metrics, + dag_accumulator, }; watch(CHAIN_WATCH_NAME, "n1251"); match uncles { @@ -141,6 +159,8 @@ impl BlockChain { storage.get_accumulator_store(AccumulatorStoreType::Block), ); let statedb = ChainStateDB::new(storage.clone().into_super_arc(), None); + + let genesis_id = genesis_block.header.id(); let executed_block = Self::execute_block_and_save( storage.as_ref(), statedb, @@ -150,7 +170,22 @@ impl BlockChain { None, genesis_block, None, + None, )?; + + let new_tips = vec![genesis_id]; + let dag_accumulator = MerkleAccumulator::new_empty( + storage.get_accumulator_store(AccumulatorStoreType::SyncDag), + ); + dag_accumulator.append(&new_tips)?; + dag_accumulator.flush()?; + storage.append_dag_accumulator_leaf( + Self::calculate_dag_accumulator_key(new_tips.clone()) + .expect("failed to calculate the dag key"), + new_tips, + dag_accumulator.get_info(), + )?; + Self::new(time_service, executed_block.block.id(), storage, None) } @@ -158,6 +193,13 @@ impl BlockChain { self.uncles.len() as u64 } + pub fn calculate_dag_accumulator_key(mut tips: Vec) -> Result { + tips.sort(); + Ok(HashValue::sha3_256_of(&tips.encode().expect( + "encoding the sorted relatship set must be successful", + ))) + } + pub fn current_block_accumulator_info(&self) -> AccumulatorInfo { self.block_accumulator.get_info() } @@ -265,6 +307,7 @@ impl BlockChain { difficulty, strategy, None, + self.status.status.tips_hash.clone(), )?; let excluded_txns = opened_block.push_txns(user_txns)?; let template = opened_block.finalize()?; @@ -327,24 +370,40 @@ impl BlockChain { V::verify_block(self, block) } - pub fn apply_with_verifier(&mut self, block: Block) -> Result + pub fn apply_with_verifier( + &mut self, + block: Block, + dag_block_parent: Option, + next_tips: &mut Option>, + ) -> Result where V: BlockVerifier, { let verified_block = self.verify_with_verifier::(block)?; watch(CHAIN_WATCH_NAME, "n1"); - let executed_block = self.execute(verified_block)?; + let executed_block = self.execute(verified_block, dag_block_parent)?; watch(CHAIN_WATCH_NAME, "n2"); - self.connect(executed_block) + self.connect(executed_block, next_tips) } //TODO remove this function. - pub fn update_chain_head(&mut self, block: Block) -> Result { + pub fn update_chain_head( + &mut self, + block: Block, + dag_parent: Option, + ) -> Result { let block_info = self .storage .get_block_info(block.id())? .ok_or_else(|| format_err!("Can not find block info by hash {:?}", block.id()))?; - self.connect(ExecutedBlock { block, block_info }) + self.connect( + ExecutedBlock { + block, + block_info, + dag_parent, + }, + &mut Some(vec![]), + ) } //TODO consider move this logic to BlockExecutor @@ -356,6 +415,7 @@ impl BlockChain { epoch: &Epoch, parent_status: Option, block: Block, + dag_block_parent: Option, vm_metrics: Option, ) -> Result { let header = block.header(); @@ -367,7 +427,8 @@ impl BlockChain { let mut t = match &parent_status { None => vec![], Some(parent) => { - let block_metadata = block.to_metadata(parent.head().gas_used()); + let block_metadata = + block.to_metadata(parent.head().gas_used(), dag_block_parent); vec![Transaction::BlockMetadata(block_metadata)] } }; @@ -506,13 +567,16 @@ impl BlockChain { storage.save_block_transaction_ids(block_id, txn_id_vec)?; storage.save_block_txn_info_ids(block_id, txn_info_ids)?; storage.commit_block(block.clone())?; - storage.save_block_info(block_info.clone())?; storage.save_table_infos(txn_table_infos)?; watch(CHAIN_WATCH_NAME, "n26"); - Ok(ExecutedBlock { block, block_info }) + Ok(ExecutedBlock { + block, + block_info, + dag_parent: dag_block_parent, + }) } pub fn get_txn_accumulator(&self) -> &MerkleAccumulator { @@ -522,6 +586,83 @@ impl BlockChain { pub fn get_block_accumulator(&self) -> &MerkleAccumulator { &self.block_accumulator } + + pub fn get_dag_leaves( + &self, + start_index: u64, + reverse: bool, + batch_size: u64, + ) -> Result> { + self.dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .get_leaves(start_index, reverse, batch_size) + } + + pub fn get_dag_current_leaf_number(&self) -> Result { + Ok(self + .dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .num_leaves()) + } + + pub fn get_dag_accumulator_snapshot(&self, key: HashValue) -> Result { + Ok(self + .storage + .query_by_hash(key)? + .expect("dag accumualator's snapshot should not be None")) + } + + pub fn get_dag_accumulator_snapshot_by_index( + &self, + leaf_index: u64, + ) -> Result { + let leaf_hash = self + .dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .get_leaf(leaf_index)? + .expect("dag accumulator's leaf should not be None"); + Ok(self + .storage + .query_by_hash(leaf_hash)? + .expect("dag accumualator's snapshot should not be None")) + } + + pub fn update_dag_accumulator(&mut self, head_id: HashValue) -> Result<()> { + self.dag_accumulator = Some( + self.dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .fork( + self.storage + .get_dag_accumulator_info(head_id) + .expect("accumulator info should not be None"), + ), + ); + Ok(()) + } + + pub fn dag_parents_in_tips(&self, dag_parents: Vec) -> Result { + Ok(dag_parents + .into_iter() + .all(|parent| match &self.status.status.tips_hash { + Some(tips) => tips.contains(&parent), + None => false, + })) + } + + pub fn is_head_of_dag_accumulator(&self, next_tips: Vec) -> Result { + let key = Self::calculate_dag_accumulator_key(next_tips)?; + let next_tips_info = self.storage.get_dag_accumulator_info(key)?; + + return Ok(next_tips_info + == self + .dag_accumulator + .as_ref() + .map(|accumulator| accumulator.get_info())); + } } impl ChainReader for BlockChain { @@ -530,6 +671,12 @@ impl ChainReader for BlockChain { self.status.head.header().chain_id(), self.genesis_hash, self.status.status.clone(), + self.storage + .get_dag_accumulator_info(self.status.head.header().id()) + .expect(&format!( + "the dag accumulator info cannot be found by id: {}", + self.status.head.header().id() + )), ) } @@ -538,13 +685,30 @@ impl ChainReader for BlockChain { } fn head_block(&self) -> ExecutedBlock { - ExecutedBlock::new(self.status.head.clone(), self.status.status.info.clone()) + ExecutedBlock::new( + self.status.head.clone(), + self.status.status.info.clone(), + self.status.status.get_last_tip_block_id(), + ) } fn current_header(&self) -> BlockHeader { self.status.status.head().clone() } + fn current_tips_hash(&self) -> Option { + match self.status.status.tips_hash.clone() { + Some(tips_hash) => { + assert!(!tips_hash.is_empty()); + Some( + Self::calculate_dag_accumulator_key(tips_hash) + .expect("calculate dag key should be successful"), + ) + } + None => None, + } + } + fn get_header(&self, hash: HashValue) -> Result> { self.storage .get_block_header_by_hash(hash) @@ -573,13 +737,12 @@ impl ChainReader for BlockChain { reverse: bool, count: u64, ) -> Result> { + let num_leaves = self.block_accumulator.num_leaves(); let end_num = match number { - None => self.current_header().number(), + None => num_leaves.saturating_sub(1), Some(number) => number, }; - let num_leaves = self.block_accumulator.num_leaves(); - if end_num > num_leaves.saturating_sub(1) { bail!("Can not find block by number {}", end_num); }; @@ -715,6 +878,7 @@ impl ChainReader for BlockChain { } else { None }; + BlockChain::new_with_uncles( self.time_service.clone(), head, @@ -755,7 +919,11 @@ impl ChainReader for BlockChain { FullVerifier::verify_block(self, block) } - fn execute(&self, verified_block: VerifiedBlock) -> Result { + fn execute( + &self, + verified_block: VerifiedBlock, + dag_block_parent: Option, + ) -> Result { Self::execute_block_and_save( self.storage.as_ref(), self.statedb.fork(), @@ -764,6 +932,7 @@ impl ChainReader for BlockChain { &self.epoch, Some(self.status.status.clone()), verified_block.0, + dag_block_parent, self.vm_metrics.clone(), ) } @@ -972,12 +1141,38 @@ impl BlockChain { impl ChainWriter for BlockChain { fn can_connect(&self, executed_block: &ExecutedBlock) -> bool { - executed_block.block.header().parent_hash() == self.status.status.head().id() + if executed_block.block.header().parent_hash() == self.status.status.head().id() { + return true; + } else { + return Self::calculate_dag_accumulator_key( + self.status + .status + .tips_hash + .as_ref() + .expect("dag blocks must have tips") + .clone(), + ) + .expect("failed to calculate the tips hash") + == executed_block.block().header().parent_hash(); + } } - fn connect(&mut self, executed_block: ExecutedBlock) -> Result { + fn connect( + &mut self, + executed_block: ExecutedBlock, + next_tips: &mut Option>, + ) -> Result { let (block, block_info) = (executed_block.block(), executed_block.block_info()); - debug_assert!(block.header().parent_hash() == self.status.status.head().id()); + if self.status.status.tips_hash.is_some() { + let mut tips = self.status.status.tips_hash.clone().unwrap(); + tips.sort(); + debug_assert!( + block.header().parent_hash() == Self::calculate_dag_accumulator_key(tips.clone())? + || block.header().parent_hash() == self.status.status.head().id() + ); + } else { + debug_assert!(block.header().parent_hash() == self.status.status.head().id()); + } //TODO try reuse accumulator and state db. let txn_accumulator_info = block_info.get_txn_accumulator_info(); let block_accumulator_info = block_info.get_block_accumulator_info(); @@ -994,8 +1189,20 @@ impl ChainWriter for BlockChain { ); self.statedb = ChainStateDB::new(self.storage.clone().into_super_arc(), Some(state_root)); + match next_tips { + Some(tips) => { + if !tips.contains(&block.header().id()) { + tips.push(block.header().id()) + } + } + None => (), + } self.status = ChainStatusWithBlock { - status: ChainStatus::new(block.header().clone(), block_info.clone()), + status: ChainStatus::new( + block.header().clone(), + block_info.clone(), + next_tips.clone(), + ), head: block.clone(), }; if self.epoch.end_block_number() == block.header().number() { @@ -1010,13 +1217,55 @@ impl ChainWriter for BlockChain { Ok(executed_block) } - fn apply(&mut self, block: Block) -> Result { - self.apply_with_verifier::(block) + fn apply( + &mut self, + block: Block, + dag_block_next_parent: Option, + next_tips: &mut Option>, + ) -> Result { + self.apply_with_verifier::(block, dag_block_next_parent, next_tips) } fn chain_state(&mut self) -> &ChainStateDB { &self.statedb } + + fn get_current_dag_accumulator_info(&self) -> Result { + Ok(self + .dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumualator is None"))? + .get_info()) + } + + fn fork_dag_accumulator(&mut self, accumulator_info: AccumulatorInfo) -> Result<()> { + self.dag_accumulator = Some( + self.dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .fork(Some(accumulator_info)), + ); + Ok(()) + } + + fn append_dag_accumulator_leaf( + &mut self, + tips: Vec, + ) -> Result<(HashValue, AccumulatorInfo)> { + let key = Self::calculate_dag_accumulator_key(tips.clone())?; + let dag_accumulator = self + .dag_accumulator + .as_ref() + .ok_or_else(|| anyhow!("dag accumulator is None"))? + .fork(None); + dag_accumulator.append(&[key])?; + dag_accumulator.flush()?; + self.storage + .append_dag_accumulator_leaf(key, tips, dag_accumulator.get_info())?; + let accumulator_info = dag_accumulator.get_info(); + self.dag_accumulator = Some(dag_accumulator); + Ok((key, accumulator_info)) + } } pub(crate) fn info_2_accumulator( diff --git a/chain/src/verifier/mod.rs b/chain/src/verifier/mod.rs index 5128715302..1d7d887f31 100644 --- a/chain/src/verifier/mod.rs +++ b/chain/src/verifier/mod.rs @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::{format_err, Result}; +use bcs_ext::BCSCodec; use sp_utils::stop_watch::{watch, CHAIN_WATCH_NAME}; use starcoin_chain_api::{ verify_block, ChainReader, ConnectBlockError, VerifiedBlock, VerifyBlockField, }; use starcoin_consensus::{Consensus, ConsensusVerifyError}; +use starcoin_crypto::HashValue; use starcoin_logger::prelude::debug; use starcoin_types::block::{Block, BlockHeader, ALLOWED_FUTURE_BLOCKTIME}; use std::{collections::HashSet, str::FromStr}; @@ -179,23 +181,52 @@ impl BlockVerifier for BasicVerifier { let current_id = current.id(); let expect_number = current.number().saturating_add(1); - verify_block!( - VerifyBlockField::Header, - expect_number == new_block_header.number(), - "Invalid block: Unexpect block number, expect:{}, got: {}.", - expect_number, - new_block_header.number() - ); - - verify_block!( - VerifyBlockField::Header, - current_id == new_block_parent, - "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", - current_id, - new_block_parent, - new_block_header.number() - ); + // dag + if chain_status.tips_hash.is_some() { + let mut tips_hash = chain_status.tips_hash.clone().unwrap(); + tips_hash.sort(); + + // if it is a dag block + if HashValue::sha3_256_of(&tips_hash.encode().expect("hash encode must be successful")) + != new_block_parent + { + // or a block of a single chain + verify_block!( + VerifyBlockField::Header, + expect_number == new_block_header.number(), + "Invalid block: Unexpect block number, expect:{}, got: {}.", + expect_number, + new_block_header.number() + ); + + verify_block!( + VerifyBlockField::Header, + current_id == new_block_parent, + "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", + current_id, + new_block_parent, + new_block_header.number() + ); + } + } else { + // or a block of a single chain + verify_block!( + VerifyBlockField::Header, + expect_number == new_block_header.number(), + "Invalid block: Unexpect block number, expect:{}, got: {}.", + expect_number, + new_block_header.number() + ); + verify_block!( + VerifyBlockField::Header, + current_id == new_block_parent, + "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", + current_id, + new_block_parent, + new_block_header.number() + ); + } verify_block!( VerifyBlockField::Header, new_block_header.timestamp() > current.timestamp(), diff --git a/chain/tests/block_test_utils.rs b/chain/tests/block_test_utils.rs index f6d7016c26..d6f5c833d0 100644 --- a/chain/tests/block_test_utils.rs +++ b/chain/tests/block_test_utils.rs @@ -260,7 +260,7 @@ proptest! { // blocks in ; for block in blocks { if !block.header().is_genesis() { - let result = block_chain.apply(block.clone()); + let result = block_chain.apply(block.clone(), None, &mut None); info!("{:?}", result); } } diff --git a/chain/tests/test_block_chain.rs b/chain/tests/test_block_chain.rs index 7b1d41411b..999e0578c3 100644 --- a/chain/tests/test_block_chain.rs +++ b/chain/tests/test_block_chain.rs @@ -131,11 +131,11 @@ fn test_block_chain() -> Result<()> { let mut mock_chain = MockChain::new(ChainNetwork::new_test())?; let block = mock_chain.produce()?; assert_eq!(block.header().number(), 1); - mock_chain.apply(block)?; + mock_chain.apply(block, None)?; assert_eq!(mock_chain.head().current_header().number(), 1); let block = mock_chain.produce()?; assert_eq!(block.header().number(), 2); - mock_chain.apply(block)?; + mock_chain.apply(block, None)?; assert_eq!(mock_chain.head().current_header().number(), 2); Ok(()) } @@ -200,7 +200,7 @@ fn gen_uncle() -> (MockChain, BlockChain, BlockHeader) { let miner = mock_chain.miner(); let block = product_a_block(&fork_block_chain, miner, Vec::new()); let uncle_block_header = block.header().clone(); - fork_block_chain.apply(block).unwrap(); + fork_block_chain.apply(block, None, &mut None).unwrap(); (mock_chain, fork_block_chain, uncle_block_header) } @@ -221,7 +221,7 @@ fn test_uncle() { // 3. mock chain apply let uncles = vec![uncle_block_header.clone()]; let block = product_a_block(mock_chain.head(), miner, uncles); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_some()); assert!(mock_chain .head() @@ -240,7 +240,7 @@ fn test_uncle_exist() { // 3. mock chain apply let uncles = vec![uncle_block_header.clone()]; let block = product_a_block(mock_chain.head(), &miner, uncles); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_some()); assert!(mock_chain .head() @@ -254,7 +254,7 @@ fn test_uncle_exist() { // 4. uncle exist let uncles = vec![uncle_block_header]; let block = product_a_block(mock_chain.head(), &miner, uncles); - assert!(mock_chain.apply(block).is_err()); + assert!(mock_chain.apply(block, None).is_err()); } #[stest::test(timeout = 120)] @@ -264,12 +264,12 @@ fn test_uncle_son() { // 3. uncle son let uncle_son = product_a_block(&fork_block_chain, miner, Vec::new()); let uncle_son_block_header = uncle_son.header().clone(); - fork_block_chain.apply(uncle_son).unwrap(); + fork_block_chain.apply(uncle_son, None).unwrap(); // 4. mock chain apply let uncles = vec![uncle_son_block_header]; let block = product_a_block(mock_chain.head(), miner, uncles); - assert!(mock_chain.apply(block).is_err()); + assert!(mock_chain.apply(block, None).is_err()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } @@ -281,7 +281,7 @@ fn test_random_uncle() { // 3. random BlockHeader and apply let uncles = vec![BlockHeader::random()]; let block = product_a_block(mock_chain.head(), miner, uncles); - assert!(mock_chain.apply(block).is_err()); + assert!(mock_chain.apply(block, None).is_err()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } @@ -293,7 +293,7 @@ fn test_switch_epoch() { // 3. mock chain apply let uncles = vec![uncle_block_header.clone()]; let block = product_a_block(mock_chain.head(), &miner, uncles); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_some()); assert!(mock_chain .head() @@ -311,14 +311,14 @@ fn test_switch_epoch() { if begin_number < (end_number - 1) { for _i in begin_number..(end_number - 1) { let block = product_a_block(mock_chain.head(), &miner, Vec::new()); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 1); } } // 5. switch epoch let block = product_a_block(mock_chain.head(), &miner, Vec::new()); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_none()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } @@ -336,21 +336,21 @@ fn test_uncle_in_diff_epoch() { if begin_number < (end_number - 1) { for _i in begin_number..(end_number - 1) { let block = product_a_block(mock_chain.head(), &miner, Vec::new()); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } } // 4. switch epoch let block = product_a_block(mock_chain.head(), &miner, Vec::new()); - mock_chain.apply(block).unwrap(); + mock_chain.apply(block, None).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_none()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); // 5. mock chain apply let uncles = vec![uncle_block_header]; let block = product_a_block(mock_chain.head(), &miner, uncles); - assert!(mock_chain.apply(block).is_err()); + assert!(mock_chain.apply(block, None).is_err()); } #[stest::test(timeout = 480)] @@ -373,7 +373,7 @@ fn test_block_chain_txn_info_fork_mapping() -> Result<()> { let block_b1 = block_chain .consensus() .create_block(template_b1, config.net().time_service().as_ref())?; - block_chain.apply(block_b1.clone())?; + block_chain.apply(block_b1.clone(), None, &mut None)?; let mut block_chain2 = block_chain.fork(block_b1.id()).unwrap(); @@ -404,7 +404,7 @@ fn test_block_chain_txn_info_fork_mapping() -> Result<()> { .consensus() .create_block(template_b2, config.net().time_service().as_ref())?; - block_chain.apply(block_b2.clone())?; + block_chain.apply(block_b2.clone(), None, &mut None)?; let (template_b3, excluded) = block_chain2.create_block_template( *miner_account.address(), Some(block_b1.id()), @@ -416,7 +416,7 @@ fn test_block_chain_txn_info_fork_mapping() -> Result<()> { let block_b3 = block_chain2 .consensus() .create_block(template_b3, config.net().time_service().as_ref())?; - block_chain2.apply(block_b3.clone())?; + block_chain2.apply(block_b3.clone(), None, &mut None)?; assert_ne!( block_chain.get_txn_accumulator().root_hash(), diff --git a/chain/tests/test_epoch_switch.rs b/chain/tests/test_epoch_switch.rs index 48143c3e9f..cf0b71d2ab 100644 --- a/chain/tests/test_epoch_switch.rs +++ b/chain/tests/test_epoch_switch.rs @@ -203,7 +203,7 @@ pub fn modify_on_chain_config_by_dao_block( .consensus() .create_block(template, chain.time_service().as_ref())?; - chain.apply(block1)?; + chain.apply(block1, None)?; } // block 2 @@ -223,7 +223,7 @@ pub fn modify_on_chain_config_by_dao_block( block_timestamp / 1000, )], )?; - chain.apply(block2)?; + chain.apply(block2, None)?; let chain_state = chain.chain_state(); let state = proposal_state( @@ -255,7 +255,7 @@ pub fn modify_on_chain_config_by_dao_block( block_timestamp / 1000, )], )?; - chain.apply(block3)?; + chain.apply(block3, None)?; } // block 4 let chain_state = chain.chain_state(); @@ -263,7 +263,7 @@ pub fn modify_on_chain_config_by_dao_block( { chain.time_service().adjust(block_timestamp); let block4 = create_new_block(&chain, &alice, vec![])?; - chain.apply(block4)?; + chain.apply(block4, None)?; let chain_state = chain.chain_state(); let quorum = quorum_vote(chain_state, stc_type_tag()); println!("quorum: {}", quorum); @@ -282,7 +282,7 @@ pub fn modify_on_chain_config_by_dao_block( let block_timestamp = block_timestamp + 20 * 1000; { chain.time_service().adjust(block_timestamp); - chain.apply(create_new_block(&chain, &alice, vec![])?)?; + chain.apply(create_new_block(&chain, &alice, vec![])?, None)?; let chain_state = chain.chain_state(); let state = proposal_state( chain_state, @@ -311,7 +311,7 @@ pub fn modify_on_chain_config_by_dao_block( block_timestamp / 1000, )], )?; - chain.apply(block6)?; + chain.apply(block6, None)?; let chain_state = chain.chain_state(); let state = proposal_state( chain_state, @@ -328,7 +328,7 @@ pub fn modify_on_chain_config_by_dao_block( let block_timestamp = block_timestamp + min_action_delay(chain_state, stc_type_tag()); { chain.time_service().adjust(block_timestamp); - chain.apply(create_new_block(&chain, &alice, vec![])?)?; + chain.apply(create_new_block(&chain, &alice, vec![])?, None)?; let chain_state = chain.chain_state(); let state = proposal_state( chain_state, @@ -353,7 +353,7 @@ pub fn modify_on_chain_config_by_dao_block( block_timestamp / 1000, )], )?; - chain.apply(block8)?; + chain.apply(block8, None)?; } // block 9 @@ -361,7 +361,7 @@ pub fn modify_on_chain_config_by_dao_block( let _chain_state = chain.chain_state(); { chain.time_service().adjust(block_timestamp); - chain.apply(create_new_block(&chain, &alice, vec![])?)?; + chain.apply(create_new_block(&chain, &alice, vec![])?, None)?; let chain_state = chain.chain_state(); let state = proposal_state( chain_state, diff --git a/chain/tests/test_opened_block.rs b/chain/tests/test_opened_block.rs index 33c922ba6b..b6c741bb6f 100644 --- a/chain/tests/test_opened_block.rs +++ b/chain/tests/test_opened_block.rs @@ -31,6 +31,7 @@ pub fn test_open_block() -> Result<()> { U256::from(0), chain.consensus(), None, + None, )? }; diff --git a/chain/tests/test_txn_info_and_proof.rs b/chain/tests/test_txn_info_and_proof.rs index d817366953..c057ef9f2b 100644 --- a/chain/tests/test_txn_info_and_proof.rs +++ b/chain/tests/test_txn_info_and_proof.rs @@ -70,9 +70,9 @@ fn test_transaction_info_and_proof() -> Result<()> { .consensus() .create_block(template, config.net().time_service().as_ref()) .unwrap(); - block_chain.apply(block.clone()).unwrap(); + block_chain.apply(block.clone(), None, &mut None).unwrap(); all_txns.push(Transaction::BlockMetadata( - block.to_metadata(current_header.gas_used()), + block.to_metadata(current_header.gas_used(), None), )); all_txns.extend(txns.into_iter().map(Transaction::UserTransaction)); current_header = block.header().clone(); diff --git a/cmd/db-exporter/src/main.rs b/cmd/db-exporter/src/main.rs index d0bf1688fb..7454f8557d 100644 --- a/cmd/db-exporter/src/main.rs +++ b/cmd/db-exporter/src/main.rs @@ -757,10 +757,14 @@ pub fn apply_block( let block_hash = block.header().id(); let block_number = block.header().number(); match verifier { - Verifier::Basic => chain.apply_with_verifier::(block)?, - Verifier::Consensus => chain.apply_with_verifier::(block)?, - Verifier::Full => chain.apply_with_verifier::(block)?, - Verifier::None => chain.apply_with_verifier::(block)?, + Verifier::Basic => { + chain.apply_with_verifier::(block, None, &mut None)? + } + Verifier::Consensus => { + chain.apply_with_verifier::(block, None, &mut None)? + } + Verifier::Full => chain.apply_with_verifier::(block, None, &mut None)?, + Verifier::None => chain.apply_with_verifier::(block, None, &mut None)?, }; // apply block then flush startup_info for breakpoint resume let startup_info = StartupInfo::new(block_hash); @@ -940,7 +944,7 @@ pub fn execute_transaction_with_create_account( println!("trans {}", block.transactions().len()); } let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -963,7 +967,7 @@ pub fn execute_transaction_with_miner_create_account( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -992,7 +996,7 @@ pub fn execute_transaction_with_miner_create_account( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1015,7 +1019,7 @@ pub fn execute_empty_transaction_with_miner( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -1042,7 +1046,7 @@ pub fn execute_empty_transaction_with_miner( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1066,7 +1070,7 @@ pub fn execute_transaction_with_fixed_account( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -1094,7 +1098,7 @@ pub fn execute_transaction_with_fixed_account( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None, &mut None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; diff --git a/cmd/peer-watcher/src/lib.rs b/cmd/peer-watcher/src/lib.rs index 0defa9ba3e..8b9d2d2500 100644 --- a/cmd/peer-watcher/src/lib.rs +++ b/cmd/peer-watcher/src/lib.rs @@ -9,6 +9,7 @@ use starcoin_network::network_p2p_handle::Networkp2pHandle; use starcoin_network::{build_network_worker, NotificationMessage}; use starcoin_storage::storage::StorageInstance; use starcoin_storage::Storage; +use starcoin_types::startup_info::ChainInfo; use std::sync::Arc; pub fn build_lighting_network( @@ -17,10 +18,16 @@ pub fn build_lighting_network( ) -> Result<(PeerInfo, NetworkWorker)> { let genesis = starcoin_genesis::Genesis::load_or_build(net)?; let storage = Arc::new(Storage::new(StorageInstance::new_cache_instance())?); - let chain_info = genesis.execute_genesis_block(net, storage)?; + let chain_info = genesis.execute_genesis_block(net, storage.clone())?; + let chain_state_info = ChainInfo::new( + chain_info.chain_id(), + chain_info.genesis_hash(), + chain_info.status().clone(), + chain_info.dag_accumulator_info().clone(), + ); build_network_worker( network_config, - chain_info, + chain_state_info, NotificationMessage::protocols(), None, None, diff --git a/cmd/peer-watcher/src/main.rs b/cmd/peer-watcher/src/main.rs index 7d455f311a..9251a9ef74 100644 --- a/cmd/peer-watcher/src/main.rs +++ b/cmd/peer-watcher/src/main.rs @@ -32,9 +32,9 @@ fn main() { rpc_protocols, version_string, } => match ChainInfo::decode(&generic_data) { - Ok(chain_info) => Some(PeerInfo::new( + Ok(chain_state_info) => Some(PeerInfo::new( remote.into(), - chain_info, + chain_state_info, notif_protocols, rpc_protocols, version_string, diff --git a/cmd/replay/src/main.rs b/cmd/replay/src/main.rs index d391c78fa3..000c038e29 100644 --- a/cmd/replay/src/main.rs +++ b/cmd/replay/src/main.rs @@ -132,18 +132,24 @@ fn main() -> anyhow::Result<()> { let block_height = block.header().number(); match opts.verifier { Verifier::Basic => { - chain2.apply_with_verifier::(block).unwrap(); + chain2 + .apply_with_verifier::(block, None, &mut None) + .unwrap(); } Verifier::Consensus => { chain2 - .apply_with_verifier::(block) + .apply_with_verifier::(block, None, &mut None) .unwrap(); } Verifier::None => { - chain2.apply_with_verifier::(block).unwrap(); + chain2 + .apply_with_verifier::(block, None, &mut None) + .unwrap(); } Verifier::Full => { - chain2.apply_with_verifier::(block).unwrap(); + chain2 + .apply_with_verifier::(block, None, &mut None) + .unwrap(); } }; println!( diff --git a/cmd/starcoin/src/dev/gen_block_cmd.rs b/cmd/starcoin/src/dev/gen_block_cmd.rs index e3156d8586..958d399112 100644 --- a/cmd/starcoin/src/dev/gen_block_cmd.rs +++ b/cmd/starcoin/src/dev/gen_block_cmd.rs @@ -1,10 +1,12 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +use std::time::Duration; + use crate::cli_state::CliState; use crate::view::{ExecuteResultView, TransactionOptions}; use crate::StarcoinOpt; -use anyhow::{ensure, Result}; +use anyhow::{anyhow, ensure, Result}; use clap::Parser; use scmd::{CommandAction, ExecContext}; use starcoin_transaction_builder::build_empty_script; diff --git a/commons/accumulator/src/node.rs b/commons/accumulator/src/node.rs index f3d05cd29c..36d8138eea 100644 --- a/commons/accumulator/src/node.rs +++ b/commons/accumulator/src/node.rs @@ -16,6 +16,7 @@ use starcoin_crypto::{ pub enum AccumulatorStoreType { Transaction, Block, + SyncDag, } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash)] diff --git a/commons/stream-task/src/collector.rs b/commons/stream-task/src/collector.rs index 3e597fce95..cd0e317bbd 100644 --- a/commons/stream-task/src/collector.rs +++ b/commons/stream-task/src/collector.rs @@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use thiserror::Error; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum CollectorState { /// Collector is enough, do not feed more item, finish task. Enough, diff --git a/commons/time-service/src/lib.rs b/commons/time-service/src/lib.rs index 9642726974..15dc51b178 100644 --- a/commons/time-service/src/lib.rs +++ b/commons/time-service/src/lib.rs @@ -163,6 +163,62 @@ impl TimeService for MockTimeService { } } +#[derive(Debug)] +pub struct DagBlockTimeWindowService { + time_service: Arc, + + time_window: u64, +} + +pub enum TimeWindowResult { + InTimeWindow, + BeforeTimeWindow, + AfterTimeWindow, +} + +impl TimeService for DagBlockTimeWindowService { + fn adjust(&self, milliseconds: u64) { + self.time_service.adjust(milliseconds) + } + + fn as_any(&self) -> &dyn Any { + self + } + fn now_secs(&self) -> u64 { + self.time_service.now_secs() + } + + fn now_millis(&self) -> u64 { + self.time_service.now_millis() + } + + fn sleep(&self, millis: u64) { + self.time_service.sleep(millis) + } +} + +impl DagBlockTimeWindowService { + pub fn new(time_windows: u64, time_service: Arc) -> Self { + Self { + time_service: time_service.clone(), + time_window: time_windows, + } + } + + pub fn is_in_time_window(&self, block_timestamp: u64) -> TimeWindowResult { + let now = self.time_service.now_millis(); + let start_time = now - now % self.time_window; + let end_time = start_time + self.time_window; + if (start_time..end_time).contains(&block_timestamp) { + TimeWindowResult::InTimeWindow + } else if block_timestamp < start_time { + TimeWindowResult::BeforeTimeWindow + } else { + TimeWindowResult::AfterTimeWindow + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/config/src/genesis_config.rs b/config/src/genesis_config.rs index f553cb5013..afb767f055 100644 --- a/config/src/genesis_config.rs +++ b/config/src/genesis_config.rs @@ -777,6 +777,7 @@ pub static G_DEV_CONFIG: Lazy = Lazy::new(|| { GenesisConfig { genesis_block_parameter: GenesisBlockParameterConfig::Static(GenesisBlockParameter { parent_hash: HashValue::sha3_256_of(b"starcoin_dev"), + // parent_hash: HashValue::from_slice(ORIGIN).expect("hash should be ok"), timestamp: 0, difficulty: 1.into(), }), diff --git a/config/src/storage_config.rs b/config/src/storage_config.rs index 38634026e0..1b33e18134 100644 --- a/config/src/storage_config.rs +++ b/config/src/storage_config.rs @@ -34,6 +34,13 @@ pub struct RocksdbConfig { pub wal_bytes_per_sync: u64, #[clap(name = "rocksdb-bytes-per-sync", long, help = "rocksdb bytes per sync")] pub bytes_per_sync: u64, + + #[clap( + name = "rocksdb-parallelism", + long, + help = "rocksdb background threads, one for default" + )] + pub parallelism: u64, } impl RocksdbConfig { @@ -61,6 +68,8 @@ impl Default for RocksdbConfig { bytes_per_sync: 1u64 << 20, // For wal sync every size to be 1MB wal_bytes_per_sync: 1u64 << 20, + // For background threads + parallelism: 1u64, } } } @@ -102,6 +111,14 @@ pub struct StorageConfig { #[serde(skip_serializing_if = "Option::is_none")] #[clap(name = "rocksdb-bytes-per-sync", long, help = "rocksdb bytes per sync")] pub bytes_per_sync: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[clap( + name = "rocksdb-parallelism", + long, + help = "rocksdb background threads" + )] + pub parallelism: Option, } impl StorageConfig { @@ -124,6 +141,7 @@ impl StorageConfig { wal_bytes_per_sync: self .wal_bytes_per_sync .unwrap_or(default.wal_bytes_per_sync), + parallelism: self.parallelism.unwrap_or(default.parallelism), } } pub fn cache_size(&self) -> usize { diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 2810b14110..1514cd0c26 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -19,11 +19,20 @@ starcoin-time-service = { workspace = true } starcoin-types = { workspace = true } starcoin-vm-types = { workspace = true } thiserror = { workspace = true } +rocksdb = { workspace = true } +bincode = { workspace = true } +serde = { workspace = true } +starcoin-storage = { workspace = true } +parking_lot = { workspace = true } +itertools = { workspace = true } +starcoin-config = { workspace = true } +bcs-ext = { workspace = true } [dev-dependencies] proptest = { workspace = true } proptest-derive = { workspace = true } stest = { workspace = true } +tempfile = { workspace = true } [features] default = [] diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 421b26bdcb..dccae700ce 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -75,11 +75,23 @@ pub trait Consensus { block_template: BlockTemplate, time_service: &dyn TimeService, ) -> Result { - let mining_hash = block_template.as_pow_header_blob(); + let mining_hash = block_template.as_pow_header_blob_single_chain(); let consensus_nonce = self.solve_consensus_nonce(&mining_hash, block_template.difficulty, time_service); let extra = BlockHeaderExtra::new([0u8; 4]); - Ok(block_template.into_block(consensus_nonce, extra)) + Ok(block_template.into_single_chain_block(consensus_nonce, extra)) + } + + fn create_single_chain_block( + &self, + block_template: BlockTemplate, + time_service: &dyn TimeService, + ) -> Result { + let mining_hash = block_template.as_pow_header_blob_single_chain(); + let consensus_nonce = + self.solve_consensus_nonce(&mining_hash, block_template.difficulty, time_service); + let extra = BlockHeaderExtra::new([0u8; 4]); + Ok(block_template.into_single_chain_block(consensus_nonce, extra)) } /// Inner helper for verify and unit testing fn verify_header_difficulty(&self, difficulty: U256, header: &BlockHeader) -> Result<()> { diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs index 8b870c6d2e..6a5bb88b95 100644 --- a/consensus/src/lib.rs +++ b/consensus/src/lib.rs @@ -23,11 +23,19 @@ pub mod cn; mod consensus; #[cfg(test)] mod consensus_test; +mod consensusdb; +pub mod dag; pub mod difficulty; pub mod dummy; pub mod keccak; pub use consensus::{Consensus, ConsensusVerifyError}; +pub use consensusdb::consensus_relations::{ + DbRelationsStore, RelationsStore, RelationsStoreReader, +}; +pub use consensusdb::prelude::{FlexiDagStorage, FlexiDagStorageConfig}; +pub use consensusdb::schema; +pub use dag::blockdag::BlockDAG; pub use starcoin_time_service::duration_since_epoch; pub fn target_to_difficulty(target: U256) -> U256 { diff --git a/miner/src/create_block_template/mod.rs b/miner/src/create_block_template/mod.rs index 5e6ba1ae50..c8f40318a2 100644 --- a/miner/src/create_block_template/mod.rs +++ b/miner/src/create_block_template/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::create_block_template::metrics::BlockBuilderMetrics; -use anyhow::{format_err, Result}; +use anyhow::{bail, format_err, Result}; use futures::executor::block_on; use starcoin_account_api::{AccountAsyncService, AccountInfo, DefaultAccountChangeEvent}; use starcoin_account_service::AccountService; @@ -111,7 +111,11 @@ impl ActorService for BlockBuilderService { impl EventHandler for BlockBuilderService { fn handle_event(&mut self, msg: NewHeadBlock, _ctx: &mut ServiceContext) { - if let Err(e) = self.inner.update_chain(msg.0.as_ref().clone()) { + let mut next_tips = msg.2.clone(); + if let Err(e) = self + .inner + .update_chain(msg.0.as_ref().clone(), &mut next_tips) + { error!("err : {:?}", e) } } @@ -240,11 +244,15 @@ where } } - pub fn update_chain(&mut self, block: ExecutedBlock) -> Result<()> { + pub fn update_chain( + &mut self, + block: ExecutedBlock, + next_tips: &mut Option>, + ) -> Result<()> { let current_header = self.chain.current_header(); let current_id = current_header.id(); if self.chain.can_connect(&block) { - self.chain.connect(block)?; + self.chain.connect(block, next_tips)?; } else { self.chain = BlockChain::new( self.chain.time_service(), @@ -312,6 +320,12 @@ where let author = *self.miner_account.address(); let previous_header = self.chain.current_header(); + + let tips_header = match self.chain.status().tips_hash { + Some(_) => self.chain.status().tips_hash, + None => bail!("currently, the chain must be dag, so the tips hash should not be None"), + }; + let uncles = self.find_uncles(); let mut now_millis = self.chain.time_service().now_millis(); if now_millis <= previous_header.timestamp() { @@ -345,6 +359,7 @@ where difficulty, strategy, self.vm_metrics.clone(), + tips_header, )?; let excluded_txns = opened_block.push_txns(txns)?; let template = opened_block.finalize()?; diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 54dfd52c12..02887c7f5c 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -251,10 +251,11 @@ impl MinerService { } if let Some(task) = self.current_task.take() { + let tips_header = task.block_template.tips_header.clone(); let block = task.finish(nonce, extra); - let block_hash = block.id(); + let block_hash: HashValue = block.id(); info!(target: "miner", "Mint new block: {}", block); - ctx.broadcast(MinedBlock(Arc::new(block))); + ctx.broadcast(MinedBlock(Arc::new(block), tips_header)); if let Some(metrics) = self.metrics.as_ref() { metrics.block_mint_count.inc(); } diff --git a/network-rpc/api/src/lib.rs b/network-rpc/api/src/lib.rs index dd4b3a909c..7732942889 100644 --- a/network-rpc/api/src/lib.rs +++ b/network-rpc/api/src/lib.rs @@ -21,6 +21,7 @@ use starcoin_types::block::{Block, BlockHeader, BlockInfo, BlockNumber}; use starcoin_types::transaction::{SignedUserTransaction, Transaction, TransactionInfo}; use starcoin_vm_types::state_store::table::TableInfo; +pub mod dag_protocol; mod remote_chain_state; pub use network_p2p_core::RawRpcClient; @@ -280,7 +281,7 @@ pub trait NetworkRpc: Sized + Send + Sync + 'static { &self, peer_id: PeerId, ids: Vec, - ) -> BoxFuture>>>; + ) -> BoxFuture>, Option)>>>>; fn get_state_with_table_item_proof( &self, @@ -288,6 +289,21 @@ pub trait NetworkRpc: Sized + Send + Sync + 'static { request: GetStateWithTableItemProof, ) -> BoxFuture>; + fn get_dag_accumulator_leaves( + &self, + peer_id: PeerId, + req: dag_protocol::GetDagAccumulatorLeaves, + ) -> BoxFuture>>; + fn get_accumulator_leaf_detail( + &self, + peer_id: PeerId, + req: dag_protocol::GetTargetDagAccumulatorLeafDetail, + ) -> BoxFuture>>>; + fn get_dag_block_info( + &self, + peer_id: PeerId, + req: dag_protocol::GetSyncDagBlockInfo, + ) -> BoxFuture>>>; fn get_state_table_info( &self, peer_id: PeerId, diff --git a/network-rpc/src/rpc.rs b/network-rpc/src/rpc.rs index c333341a44..1d6ca41e78 100644 --- a/network-rpc/src/rpc.rs +++ b/network-rpc/src/rpc.rs @@ -10,10 +10,10 @@ use starcoin_accumulator::AccumulatorNode; use starcoin_chain_service::{ChainAsyncService, ChainReaderService}; use starcoin_crypto::HashValue; use starcoin_network_rpc_api::{ - gen_server, BlockBody, GetAccountState, GetAccumulatorNodeByNodeHash, GetBlockHeadersByNumber, - GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, GetTableInfo, GetTxnsWithHash, - GetTxnsWithSize, Ping, RpcRequest, MAX_BLOCK_HEADER_REQUEST_SIZE, MAX_BLOCK_INFO_REQUEST_SIZE, - MAX_BLOCK_REQUEST_SIZE, MAX_TXN_REQUEST_SIZE, + dag_protocol, gen_server, BlockBody, GetAccountState, GetAccumulatorNodeByNodeHash, + GetBlockHeadersByNumber, GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, + GetTableInfo, GetTxnsWithHash, GetTxnsWithSize, Ping, RpcRequest, MAX_BLOCK_HEADER_REQUEST_SIZE, + MAX_BLOCK_INFO_REQUEST_SIZE, MAX_BLOCK_REQUEST_SIZE, MAX_TXN_REQUEST_SIZE, }; use starcoin_service_registry::ServiceRef; use starcoin_state_api::{ChainStateAsyncService, StateWithProof, StateWithTableItemProof}; @@ -196,7 +196,7 @@ impl gen_server::NetworkRpc for NetworkRpcImpl { let blocks = chain_reader.get_blocks(hashes).await?; let mut bodies = vec![]; for block in blocks { - bodies.push(block.map(|block| block.body)); + bodies.push(block.map(|(block, _, _)| block.body)); } Ok(bodies) }; @@ -303,7 +303,7 @@ impl gen_server::NetworkRpc for NetworkRpcImpl { &self, _peer_id: PeerId, ids: Vec, - ) -> BoxFuture>>> { + ) -> BoxFuture>, Option)>>>> { let chain_service = self.chain_service.clone(); let fut = async move { if ids.len() as u64 > MAX_BLOCK_REQUEST_SIZE { @@ -317,4 +317,32 @@ impl gen_server::NetworkRpc for NetworkRpcImpl { }; Box::pin(fut) } + + fn get_dag_accumulator_leaves( + &self, + _peer_id: PeerId, + req: dag_protocol::GetDagAccumulatorLeaves, + ) -> BoxFuture>> { + let chain_service = self.chain_service.clone(); + let fut = async move { chain_service.get_dag_accumulator_leaves(req).await }; + Box::pin(fut) + } + + fn get_accumulator_leaf_detail( + &self, + _peer_id: PeerId, + req: dag_protocol::GetTargetDagAccumulatorLeafDetail, + ) -> BoxFuture>>> { + let chain_service = self.chain_service.clone(); + let fut = async move { chain_service.get_dag_accumulator_leaves_detail(req).await }; + Box::pin(fut) + } + + fn get_dag_block_info( + &self, + _peer_id: PeerId, + _req: dag_protocol::GetSyncDagBlockInfo, + ) -> BoxFuture>>> { + todo!() + } } diff --git a/network/api/src/messages.rs b/network/api/src/messages.rs index 046fb58e77..6f51a79b2d 100644 --- a/network/api/src/messages.rs +++ b/network/api/src/messages.rs @@ -48,20 +48,30 @@ impl Sample for TransactionsMessage { pub struct CompactBlockMessage { pub compact_block: CompactBlock, pub block_info: BlockInfo, + pub tips_header: Option>, } impl CompactBlockMessage { - pub fn new(compact_block: CompactBlock, block_info: BlockInfo) -> Self { + pub fn new( + compact_block: CompactBlock, + block_info: BlockInfo, + tips_header: Option>, + ) -> Self { Self { compact_block, block_info, + tips_header, } } } impl Sample for CompactBlockMessage { fn sample() -> Self { - Self::new(CompactBlock::sample(), BlockInfo::sample()) + Self::new( + CompactBlock::sample(), + BlockInfo::sample(), + Some(vec![HashValue::zero()]), + ) } } diff --git a/network/api/src/tests.rs b/network/api/src/tests.rs index 801277064e..cf18f2ce9b 100644 --- a/network/api/src/tests.rs +++ b/network/api/src/tests.rs @@ -3,10 +3,12 @@ use crate::peer_provider::{PeerSelector, PeerStrategy}; use crate::peer_score::{InverseScore, Score}; +use bcs_ext::Sample; use network_p2p_types::peer_id::PeerId; use network_types::peer_info::PeerInfo; use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; +use starcoin_types::dag_block::AccumulatorInfo; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; use starcoin_types::U256; @@ -34,28 +36,48 @@ fn test_peer_selector() { let peers = vec![ PeerInfo::new( PeerId::random(), - ChainInfo::new(1.into(), HashValue::zero(), mock_chain_status(100.into())), + ChainInfo::new( + 1.into(), + HashValue::zero(), + mock_chain_status(100.into()), + None, + ), vec![], vec![], None, ), PeerInfo::new( PeerId::random(), - ChainInfo::new(1.into(), HashValue::zero(), mock_chain_status(99.into())), + ChainInfo::new( + 1.into(), + HashValue::zero(), + mock_chain_status(99.into()), + None, + ), vec![], vec![], None, ), PeerInfo::new( PeerId::random(), - ChainInfo::new(1.into(), HashValue::zero(), mock_chain_status(100.into())), + ChainInfo::new( + 1.into(), + HashValue::zero(), + mock_chain_status(100.into()), + None, + ), vec![], vec![], None, ), PeerInfo::new( PeerId::random(), - ChainInfo::new(1.into(), HashValue::zero(), mock_chain_status(1.into())), + ChainInfo::new( + 1.into(), + HashValue::zero(), + mock_chain_status(1.into()), + None, + ), vec![], vec![], None, diff --git a/network/src/network_p2p_handle.rs b/network/src/network_p2p_handle.rs index 0c58124c82..bfb5458f22 100644 --- a/network/src/network_p2p_handle.rs +++ b/network/src/network_p2p_handle.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; /// Current protocol version. -pub(crate) const CURRENT_VERSION: u32 = 5; +pub(crate) const CURRENT_VERSION: u32 = 6; /// Lowest version we support pub(crate) const MIN_VERSION: u32 = 3; @@ -123,9 +123,9 @@ impl BusinessLayerHandle for Networkp2pHandle { } fn update_status(&mut self, peer_status: &[u8]) -> Result<(), anyhow::Error> { - match ChainStatus::decode(peer_status) { - std::result::Result::Ok(status) => { - self.status.info.update_status(status); + match ChainInfo::decode(peer_status) { + std::result::Result::Ok(chain_info) => { + self.status.info = chain_info; Ok(()) } Err(error) => { diff --git a/network/src/service.rs b/network/src/service.rs index e4478f5a03..1b808e7d6c 100644 --- a/network/src/service.rs +++ b/network/src/service.rs @@ -56,7 +56,7 @@ impl NetworkActor for NetworkActorService {} impl NetworkActorService { pub fn new( config: Arc, - chain_info: ChainInfo, + chain_state_info: ChainInfo, rpc: Option<(RpcInfo, ServiceRef)>, peer_message_handler: H, ) -> Result @@ -65,7 +65,7 @@ impl NetworkActorService { { let (self_info, worker) = build_network_worker( &config.network, - chain_info, + chain_state_info, config.network.supported_network_protocols(), rpc, config.metrics.registry().cloned(), @@ -488,7 +488,10 @@ impl Inner { self.self_peer .peer_info .update_chain_status(chain_status.clone()); - match chain_status.encode() { + self.self_peer + .peer_info + .update_dag_accumulator_info(sync_status.dag_accumulator_info().clone()); + match self.self_peer.peer_info.chain_info.encode() { Ok(status) => { self.network_service.update_business_status(status); } @@ -554,8 +557,9 @@ impl Inner { ); peer_info.known_blocks.put(block_id, ()); peer_info.peer_info.update_chain_status(ChainStatus::new( - block_header, + block_header.clone(), compact_block_message.block_info.clone(), + compact_block_message.tips_header.clone(), )); if self.self_peer.known_blocks.contains(&block_id) { @@ -714,11 +718,13 @@ impl Inner { //2. Sync status change. // may be update by repeat message, but can not find a more good way. self.network_service.update_business_status( - ChainStatus::new(msg.compact_block.header.clone(), msg.block_info.clone()) - .encode() - .expect( - "Encoding the compact_block.header and block_info must be successful", - ), + ChainStatus::new( + msg.compact_block.header.clone(), + msg.block_info.clone(), + msg.tips_header.clone(), + ) + .encode() + .expect("Encoding the compact_block.header and block_info must be successful"), ); self.self_peer.known_blocks.put(id, ()); diff --git a/network/tests/network_service_test.rs b/network/tests/network_service_test.rs index 0b2ca2958a..15b6b9318f 100644 --- a/network/tests/network_service_test.rs +++ b/network/tests/network_service_test.rs @@ -38,6 +38,7 @@ fn build_test_network_services(num: usize) -> Vec { BuiltinNetworkID::Test.chain_id(), HashValue::random(), ChainStatus::random(), + None, ); for _index in 0..num { let mut boot_nodes = Vec::new(); @@ -157,6 +158,7 @@ async fn test_event_notify_receive() { CompactBlockMessage::new( CompactBlock::new(Block::new(BlockHeader::random(), BlockBody::new_empty())), mock_block_info(1.into()), + Some(vec![HashValue::zero()]), ), ); let mut receiver = network2.message_handler.channel(); @@ -173,12 +175,20 @@ async fn test_event_notify_receive_repeat_block() { let msg_send1 = PeerMessage::new_compact_block( network2.peer_id(), - CompactBlockMessage::new(CompactBlock::new(block.clone()), mock_block_info(1.into())), + CompactBlockMessage::new( + CompactBlock::new(block.clone()), + mock_block_info(1.into()), + Some(vec![HashValue::zero()]), + ), ); let msg_send2 = PeerMessage::new_compact_block( network2.peer_id(), - CompactBlockMessage::new(CompactBlock::new(block.clone()), mock_block_info(1.into())), + CompactBlockMessage::new( + CompactBlock::new(block.clone()), + mock_block_info(1.into()), + Some(vec![HashValue::zero()]), + ), ); let mut receiver = network2.message_handler.channel(); @@ -264,6 +274,7 @@ async fn test_event_broadcast() { CompactBlock::new(block.clone()), //difficulty should > genesis block difficulty. mock_block_info(10.into()), + Some(vec![HashValue::zero()]), ))); node1.service_ref.broadcast(notification.clone()); diff --git a/network/types/src/peer_info.rs b/network/types/src/peer_info.rs index 1ab7bdd70e..13b0463afb 100644 --- a/network/types/src/peer_info.rs +++ b/network/types/src/peer_info.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use starcoin_crypto::HashValue; use starcoin_types::block::BlockHeader; use starcoin_types::block::BlockNumber; +use starcoin_types::dag_block::AccumulatorInfo; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; use starcoin_types::U256; use std::borrow::Cow; @@ -67,6 +68,11 @@ impl PeerInfo { self.chain_info.update_status(chain_status) } + pub fn update_dag_accumulator_info(&mut self, dag_accumulator_info: Option) { + self.chain_info + .update_dag_accumulator_info(dag_accumulator_info) + } + /// This peer is support notification pub fn is_support_notification(&self) -> bool { !self.notif_protocols.is_empty() diff --git a/node/Cargo.toml b/node/Cargo.toml index cc9c4797fa..f8d94cc883 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -47,6 +47,7 @@ starcoin-vm-runtime = { workspace = true } thiserror = { workspace = true } timeout-join-handler = { workspace = true } tokio = { features = ["full"], workspace = true } +starcoin-accumulator = { workspace = true } num_cpus = { workspace = true } [dev-dependencies] diff --git a/node/src/node.rs b/node/src/node.rs index 34b7cc20a7..5e00d55aa6 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -17,6 +17,8 @@ use starcoin_block_relayer::BlockRelayer; use starcoin_chain_notify::ChainNotifyHandlerService; use starcoin_chain_service::ChainReaderService; use starcoin_config::NodeConfig; +use starcoin_consensus::{BlockDAG, FlexiDagStorage, FlexiDagStorageConfig}; +use starcoin_crypto::HashValue; use starcoin_genesis::{Genesis, GenesisError}; use starcoin_logger::prelude::*; use starcoin_logger::structured_log::init_slog_logger; @@ -52,9 +54,11 @@ use starcoin_sync::sync::SyncService; use starcoin_sync::txn_sync::TxnSyncService; use starcoin_sync::verified_rpc_client::VerifiedRpcClient; use starcoin_txpool::{TxPoolActorService, TxPoolService}; +use starcoin_types::blockhash::ORIGIN; +use starcoin_types::header::Header; use starcoin_types::system_events::{SystemShutdown, SystemStarted}; use starcoin_vm_runtime::metrics::VMMetrics; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; pub struct NodeService { @@ -133,12 +137,32 @@ impl ServiceHandler for NodeService { .start_service_sync(GenerateBlockEventPacemaker::service_name()), ), NodeRequest::ResetNode(block_hash) => { - let connect_service = ctx - .service_ref::>()? - .clone(); + let connect_service = ctx.service_ref::()?.clone(); + let dag = ctx + .get_shared::>() + .expect("ghost dag object does not exits"); + let parents = match dag.get_parents(block_hash) { + Ok(parents) => { + if parents.is_empty() { + None + } else { + Some(parents) + } + } + Err(error) => { + error!("Get parents error: {:?}", error); + None + } + }; + let fut = async move { info!("Prepare to reset node startup info to {}", block_hash); - connect_service.send(ResetRequest { block_hash }).await? + connect_service + .send(ResetRequest { + block_hash, + dag_block_parent: parents, + }) + .await? }; let receiver = ctx.exec(fut); NodeResponse::AsyncResult(receiver) @@ -153,6 +177,23 @@ impl ServiceHandler for NodeService { .service_ref::>()? .clone(); let network = ctx.get_shared::()?; + let dag = ctx + .get_shared::>() + .expect("ghost dag object does not exits"); + let parents = match dag.get_parents(block_hash) { + Ok(parents) => { + if parents.is_empty() { + None + } else { + Some(parents) + } + } + Err(error) => { + error!("Get parents error: {:?}", error); + None + } + }; + // let dag_transaction_parent = storage.get_accumulator_store(AccumulatorStoreType::Block).?; let fut = async move { info!("Prepare to re execute block {}", block_hash); let block = match storage.get_block(block_hash)? { @@ -170,7 +211,7 @@ impl ServiceHandler for NodeService { peer_selector.retain_rpc_peers(); let rpc_client = VerifiedRpcClient::new(peer_selector, network); let mut blocks = rpc_client.get_blocks(vec![block_hash]).await?; - blocks.pop().flatten().map(|(block, _peer)| block) + blocks.pop().flatten().map(|(block, _peer, _, _)| block) } } }; @@ -180,7 +221,13 @@ impl ServiceHandler for NodeService { block_hash ) })?; - let result = connect_service.send(ExecuteRequest { block }).await??; + let result = connect_service + .send(ExecuteRequest { + block, + dag_block_parent: parents, + dag_transaction_parent: None, + }) + .await??; info!("Re execute result: {:?}", result); Ok(()) }; @@ -315,9 +362,24 @@ impl NodeService { let upgrade_time = SystemTime::now().duration_since(start_time)?; let storage = Arc::new(Storage::new(storage_instance)?); registry.put_shared(storage.clone()).await?; + let (chain_info, genesis) = Genesis::init_and_check_storage(config.net(), storage.clone(), config.data_dir())?; + let flex_dag_config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); + let flex_dag_db = FlexiDagStorage::create_from_path("./smolstc", flex_dag_config) + .expect("Failed to create flexidag storage"); + + let dag = BlockDAG::new( + Header::new( + genesis.block().header().clone(), + vec![HashValue::new(ORIGIN)], + ), + 3, + flex_dag_db, + ); + registry.put_shared(Arc::new(Mutex::new(dag))).await?; + info!( "Start node with chain info: {}, number {} upgrade_time cost {} secs, ", chain_info, diff --git a/rpc/server/src/module/chain_rpc.rs b/rpc/server/src/module/chain_rpc.rs index 3544155169..eea1652095 100644 --- a/rpc/server/src/module/chain_rpc.rs +++ b/rpc/server/src/module/chain_rpc.rs @@ -73,7 +73,7 @@ where let fut = async move { let chain_status = service.main_status().await?; //TODO get chain info from chain service. - Ok(ChainInfo::new(chain_id, genesis_hash, chain_status).into()) + Ok(ChainInfo::new(chain_id, genesis_hash, chain_status, None).into()) }; Box::pin(fut.boxed().map_err(map_err)) } diff --git a/rpc/server/src/module/pubsub/tests.rs b/rpc/server/src/module/pubsub/tests.rs index fc5d74cc7d..ae2b966064 100644 --- a/rpc/server/src/module/pubsub/tests.rs +++ b/rpc/server/src/module/pubsub/tests.rs @@ -66,7 +66,7 @@ pub async fn test_subscribe_to_events() -> Result<()> { let new_block = block_chain .consensus() .create_block(block_template, net.time_service().as_ref())?; - let executed_block = block_chain.apply(new_block.clone())?; + let executed_block = block_chain.apply(new_block.clone(), None)?; let reader = block_chain.chain_state_reader(); let balance = reader.get_balance(account_address)?; @@ -109,7 +109,7 @@ pub async fn test_subscribe_to_events() -> Result<()> { // send block let block_detail = Arc::new(executed_block); - bus.broadcast(NewHeadBlock(block_detail))?; + bus.broadcast(NewHeadBlock(block_detail, None, None))?; let mut receiver = receiver; diff --git a/state/service/src/service.rs b/state/service/src/service.rs index f54738a1e8..b6746a9b8e 100644 --- a/state/service/src/service.rs +++ b/state/service/src/service.rs @@ -131,7 +131,7 @@ impl ServiceHandler for ChainStateService { impl EventHandler for ChainStateService { fn handle_event(&mut self, msg: NewHeadBlock, _ctx: &mut ServiceContext) { - let NewHeadBlock(block) = msg; + let NewHeadBlock(block, _dag_parents, _next_tips) = msg; let state_root = block.header().state_root(); debug!("ChainStateActor change StateRoot to : {:?}", state_root); diff --git a/storage/src/accumulator/mod.rs b/storage/src/accumulator/mod.rs index fbbb6bc37e..594e5681d1 100644 --- a/storage/src/accumulator/mod.rs +++ b/storage/src/accumulator/mod.rs @@ -1,9 +1,9 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::define_storage; use crate::storage::{CodecKVStore, ValueCodec}; use crate::StorageInstance; +use crate::{define_storage, SYNC_FLEXI_DAG_ACCUMULATOR_PREFIX_NAME}; use crate::{BLOCK_ACCUMULATOR_NODE_PREFIX_NAME, TRANSACTION_ACCUMULATOR_NODE_PREFIX_NAME}; use anyhow::Result; use bcs_ext::BCSCodec; @@ -24,6 +24,13 @@ define_storage!( TRANSACTION_ACCUMULATOR_NODE_PREFIX_NAME ); +define_storage!( + DagBlockAccumulatorStorage, + HashValue, + AccumulatorNode, + SYNC_FLEXI_DAG_ACCUMULATOR_PREFIX_NAME +); + impl ValueCodec for AccumulatorNode { fn encode_value(&self) -> Result> { self.encode() @@ -62,6 +69,16 @@ impl AccumulatorStorage { } } +impl AccumulatorStorage { + pub fn new_dag_block_accumulator_storage( + instance: StorageInstance, + ) -> AccumulatorStorage { + Self { + store: DagBlockAccumulatorStorage::new(instance), + } + } +} + impl AccumulatorTreeStore for AccumulatorStorage where S: CodecKVStore, diff --git a/storage/src/batch/mod.rs b/storage/src/batch/mod.rs index 60e463274e..562ed71ae1 100644 --- a/storage/src/batch/mod.rs +++ b/storage/src/batch/mod.rs @@ -5,29 +5,31 @@ use crate::storage::{CodecWriteBatch, KeyCodec, ValueCodec, WriteOp}; use anyhow::Result; use std::convert::TryFrom; +pub type WriteBatch = GWriteBatch, Vec>; + #[derive(Debug, Default, Clone)] -pub struct WriteBatch { - pub rows: Vec<(Vec, WriteOp>)>, +pub struct GWriteBatch { + pub rows: Vec<(K, WriteOp)>, } -impl WriteBatch { +impl GWriteBatch { /// Creates an empty batch. pub fn new() -> Self { Self::default() } - pub fn new_with_rows(rows: Vec<(Vec, WriteOp>)>) -> Self { + pub fn new_with_rows(rows: Vec<(K, WriteOp)>) -> Self { Self { rows } } /// Adds an insert/update operation to the batch. - pub fn put(&mut self, key: Vec, value: Vec) -> Result<()> { + pub fn put(&mut self, key: K, value: V) -> Result<()> { self.rows.push((key, WriteOp::Value(value))); Ok(()) } /// Adds a delete operation to the batch. - pub fn delete(&mut self, key: Vec) -> Result<()> { + pub fn delete(&mut self, key: K) -> Result<()> { self.rows.push((key, WriteOp::Deletion)); Ok(()) } diff --git a/storage/src/block/mod.rs b/storage/src/block/mod.rs index 9b2f162ba6..580550a9ee 100644 --- a/storage/src/block/mod.rs +++ b/storage/src/block/mod.rs @@ -1,7 +1,7 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::define_storage; use crate::storage::{CodecKVStore, StorageInstance, ValueCodec}; +use crate::{define_storage, BLOCK_TIPS_HEADER_PREFIX_NAME}; use crate::{ BLOCK_BODY_PREFIX_NAME, BLOCK_HEADER_PREFIX_NAME, BLOCK_PREFIX_NAME, BLOCK_TRANSACTIONS_PREFIX_NAME, BLOCK_TRANSACTION_INFOS_PREFIX_NAME, FAILED_BLOCK_PREFIX_NAME, @@ -82,6 +82,14 @@ define_storage!( BlockHeader, BLOCK_HEADER_PREFIX_NAME ); + +define_storage!( + BlockTipsHeaderStorage, + HashValue, + Vec, + BLOCK_TIPS_HEADER_PREFIX_NAME +); + define_storage!( BlockBodyStorage, HashValue, @@ -111,6 +119,7 @@ define_storage!( pub struct BlockStorage { block_store: BlockInnerStorage, pub(crate) header_store: BlockHeaderStorage, + pub(crate) tips_header_store: BlockTipsHeaderStorage, body_store: BlockBodyStorage, block_txns_store: BlockTransactionsStorage, block_txn_infos_store: BlockTransactionInfosStorage, @@ -137,6 +146,16 @@ impl ValueCodec for BlockHeader { } } +impl ValueCodec for Vec { + fn encode_value(&self) -> Result> { + self.encode() + } + + fn decode_value(data: &[u8]) -> Result { + Self::decode(data) + } +} + impl ValueCodec for BlockBody { fn encode_value(&self) -> Result> { self.encode() @@ -171,6 +190,7 @@ impl BlockStorage { BlockStorage { block_store: BlockInnerStorage::new(instance.clone()), header_store: BlockHeaderStorage::new(instance.clone()), + tips_header_store: BlockTipsHeaderStorage::new(instance.clone()), body_store: BlockBodyStorage::new(instance.clone()), block_txns_store: BlockTransactionsStorage::new(instance.clone()), block_txn_infos_store: BlockTransactionInfosStorage::new(instance.clone()), @@ -236,6 +256,13 @@ impl BlockStorage { self.header_store.get(block_id) } + pub fn get_block_tips_header_by_hash( + &self, + block_id: HashValue, + ) -> Result>> { + self.tips_header_store.get(block_id) + } + pub fn get_block_by_hash(&self, block_id: HashValue) -> Result> { self.get(block_id) } diff --git a/storage/src/cache_storage/mod.rs b/storage/src/cache_storage/mod.rs index 46001ba401..596fbd181d 100644 --- a/storage/src/cache_storage/mod.rs +++ b/storage/src/cache_storage/mod.rs @@ -1,34 +1,44 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::batch::WriteBatch; -use crate::metrics::{record_metrics, StorageMetrics}; -use crate::storage::{InnerStore, WriteOp}; +use crate::batch::GWriteBatch; +use crate::{ + batch::WriteBatch, + metrics::{record_metrics, StorageMetrics}, + storage::{InnerStore, WriteOp}, +}; use anyhow::{Error, Result}; +use core::hash::Hash; use lru::LruCache; use parking_lot::Mutex; use starcoin_config::DEFAULT_CACHE_SIZE; -pub struct CacheStorage { - cache: Mutex, Vec>>, + +pub type CacheStorage = GCacheStorage, Vec>; + +pub struct GCacheStorage { + cache: Mutex>, metrics: Option, } -impl CacheStorage { +impl GCacheStorage { pub fn new(metrics: Option) -> Self { - CacheStorage { - cache: Mutex::new(LruCache::new(DEFAULT_CACHE_SIZE)), + GCacheStorage { + cache: Mutex::new(LruCache::::new(DEFAULT_CACHE_SIZE)), metrics, } } pub fn new_with_capacity(size: usize, metrics: Option) -> Self { - CacheStorage { - cache: Mutex::new(LruCache::new(size)), + GCacheStorage { + cache: Mutex::new(LruCache::::new(size)), metrics, } } + pub fn remove_all(&self) { + self.cache.lock().clear(); + } } -impl Default for CacheStorage { +impl Default for GCacheStorage { fn default() -> Self { Self::new(None) } @@ -36,53 +46,47 @@ impl Default for CacheStorage { impl InnerStore for CacheStorage { fn get(&self, prefix_name: &str, key: Vec) -> Result>> { - record_metrics("cache", prefix_name, "get", self.metrics.as_ref()).call(|| { - Ok(self - .cache - .lock() - .get(&compose_key(prefix_name.to_string(), key)) - .cloned()) - }) + let composed_key = compose_key(Some(prefix_name), key); + record_metrics("cache", prefix_name, "get", self.metrics.as_ref()) + .call(|| Ok(self.get_inner(&composed_key))) } fn put(&self, prefix_name: &str, key: Vec, value: Vec) -> Result<()> { // remove record_metrics for performance // record_metrics add in write_batch to reduce Instant::now system call - let mut cache = self.cache.lock(); - cache.put(compose_key(prefix_name.to_string(), key), value); + let composed_key = compose_key(Some(prefix_name), key); + let len = self.put_inner(composed_key, value); if let Some(metrics) = self.metrics.as_ref() { - metrics.cache_items.set(cache.len() as u64); + metrics.cache_items.set(len as u64); } Ok(()) } fn contains_key(&self, prefix_name: &str, key: Vec) -> Result { - record_metrics("cache", prefix_name, "contains_key", self.metrics.as_ref()).call(|| { - Ok(self - .cache - .lock() - .contains(&compose_key(prefix_name.to_string(), key))) - }) + let composed_key = compose_key(Some(prefix_name), key); + record_metrics("cache", prefix_name, "contains_key", self.metrics.as_ref()) + .call(|| Ok(self.contains_key_inner(&composed_key))) } fn remove(&self, prefix_name: &str, key: Vec) -> Result<()> { // remove record_metrics for performance // record_metrics add in write_batch to reduce Instant::now system call - let mut cache = self.cache.lock(); - cache.pop(&compose_key(prefix_name.to_string(), key)); + let composed_key = compose_key(Some(prefix_name), key); + let len = self.remove_inner(&composed_key); if let Some(metrics) = self.metrics.as_ref() { - metrics.cache_items.set(cache.len() as u64); + metrics.cache_items.set(len as u64); } Ok(()) } fn write_batch(&self, prefix_name: &str, batch: WriteBatch) -> Result<()> { + let rows = batch + .rows + .into_iter() + .map(|(k, v)| (compose_key(Some(prefix_name), k), v)) + .collect(); + let batch = WriteBatch { rows }; record_metrics("cache", prefix_name, "write_batch", self.metrics.as_ref()).call(|| { - for (key, write_op) in &batch.rows { - match write_op { - WriteOp::Value(value) => self.put(prefix_name, key.to_vec(), value.to_vec())?, - WriteOp::Deletion => self.remove(prefix_name, key.to_vec())?, - }; - } + self.write_batch_inner(batch); Ok(()) }) } @@ -108,22 +112,76 @@ impl InnerStore for CacheStorage { } fn multi_get(&self, prefix_name: &str, keys: Vec>) -> Result>>> { + let composed_keys = keys + .into_iter() + .map(|k| compose_key(Some(prefix_name), k)) + .collect::>(); + Ok(self.multi_get_inner(composed_keys.as_slice())) + } +} + +fn compose_key(prefix_name: Option<&str>, source_key: Vec) -> Vec { + match prefix_name { + Some(prefix_name) => { + let temp_vec = prefix_name.as_bytes().to_vec(); + let mut compose = Vec::with_capacity(temp_vec.len() + source_key.len()); + compose.extend(temp_vec); + compose.extend(source_key); + compose + } + None => source_key, + } +} + +impl GCacheStorage { + pub fn get_inner(&self, key: &K) -> Option { + self.cache.lock().get(key).cloned() + } + + pub fn put_inner(&self, key: K, value: V) -> usize { + let mut cache = self.cache.lock(); + cache.put(key, value); + cache.len() + } + + pub fn contains_key_inner(&self, key: &K) -> bool { + self.cache.lock().contains(key) + } + + pub fn remove_inner(&self, key: &K) -> usize { + let mut cache = self.cache.lock(); + cache.pop(key); + cache.len() + } + + pub fn write_batch_inner(&self, batch: GWriteBatch) { + for (key, write_op) in batch.rows { + match write_op { + WriteOp::Value(value) => { + self.put_inner(key, value); + } + WriteOp::Deletion => { + self.remove_inner(&key); + } + }; + } + } + + pub fn put_sync_inner(&self, key: K, value: V) -> usize { + self.put_inner(key, value) + } + + pub fn write_batch_sync_inner(&self, batch: GWriteBatch) { + self.write_batch_inner(batch) + } + + pub fn multi_get_inner(&self, keys: &[K]) -> Vec> { let mut cache = self.cache.lock(); let mut result = vec![]; - for key in keys.into_iter() { - let item = cache - .get(&compose_key(prefix_name.to_string(), key)) - .cloned(); + for key in keys { + let item = cache.get(key).cloned(); result.push(item); } - Ok(result) + result } } - -fn compose_key(prefix_name: String, source_key: Vec) -> Vec { - let temp_vec = prefix_name.as_bytes().to_vec(); - let mut compose = Vec::with_capacity(temp_vec.len() + source_key.len()); - compose.extend(temp_vec); - compose.extend(source_key); - compose -} diff --git a/storage/src/chain_info/mod.rs b/storage/src/chain_info/mod.rs index 3f193be3f0..c83ce383ff 100644 --- a/storage/src/chain_info/mod.rs +++ b/storage/src/chain_info/mod.rs @@ -28,6 +28,22 @@ impl ChainInfoStorage { const STORAGE_VERSION_KEY: &'static str = "storage_version"; const SNAPSHOT_RANGE_KEY: &'static str = "snapshot_height"; const BARNARD_HARD_FORK: &'static str = "barnard_hard_fork"; + const FLEXI_DAG_STARTUP_INFO_KEY: &'static str = "flexi_dag_startup_info"; + + pub fn get_flexi_dag_startup_info(&self) -> Result> { + self.get(Self::FLEXI_DAG_STARTUP_INFO_KEY.as_bytes()) + .and_then(|bytes| match bytes { + Some(bytes) => Ok(Some(bytes.try_into()?)), + None => Ok(None), + }) + } + + pub fn save_flexi_dag_startup_info(&self, startup_info: StartupInfo) -> Result<()> { + self.put_sync( + Self::FLEXI_DAG_STARTUP_INFO_KEY.as_bytes().to_vec(), + startup_info.try_into()?, + ) + } pub fn get_startup_info(&self) -> Result> { self.get(Self::STARTUP_INFO_KEY.as_bytes()) diff --git a/storage/src/db_storage/mod.rs b/storage/src/db_storage/mod.rs index 20e6f82dbc..e80a870544 100644 --- a/storage/src/db_storage/mod.rs +++ b/storage/src/db_storage/mod.rs @@ -1,18 +1,20 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::batch::WriteBatch; -use crate::errors::StorageInitError; -use crate::metrics::{record_metrics, StorageMetrics}; -use crate::storage::{ColumnFamilyName, InnerStore, KeyCodec, ValueCodec, WriteOp}; -use crate::{StorageVersion, DEFAULT_PREFIX_NAME}; +use crate::{ + batch::WriteBatch, + errors::StorageInitError, + metrics::{record_metrics, StorageMetrics}, + storage::{ColumnFamilyName, InnerStore, KeyCodec, RawDBStorage, ValueCodec, WriteOp}, + StorageVersion, DEFAULT_PREFIX_NAME, +}; use anyhow::{ensure, format_err, Error, Result}; -use rocksdb::{Options, ReadOptions, WriteBatch as DBWriteBatch, WriteOptions, DB}; +use rocksdb::{ + DBIterator, DBPinnableSlice, IteratorMode, Options, ReadOptions, WriteBatch as DBWriteBatch, + WriteOptions, DB, +}; use starcoin_config::{check_open_fds_limit, RocksdbConfig}; -use std::collections::HashSet; -use std::iter; -use std::marker::PhantomData; -use std::path::Path; +use std::{collections::HashSet, iter, marker::PhantomData, path::Path}; const RES_FDS: u64 = 4096; @@ -213,6 +215,9 @@ impl DBStorage { // write buffer size db_opts.set_max_write_buffer_number(5); db_opts.set_max_background_jobs(5); + if config.parallelism > 1 { + db_opts.increase_parallelism(config.parallelism as i32); + } // cache // let cache = Cache::new_lru_cache(2 * 1024 * 1024 * 1024); // db_opts.set_row_cache(&cache.unwrap()); @@ -235,6 +240,16 @@ impl DBStorage { )) } + pub fn raw_iterator_cf_opt( + &self, + prefix_name: &str, + mode: IteratorMode, + readopts: ReadOptions, + ) -> Result { + let cf_handle = self.get_cf_handle(prefix_name)?; + Ok(self.db.iterator_cf_opt(cf_handle, readopts, mode)) + } + /// Returns a forward [`SchemaIterator`] on a certain schema. pub fn iter(&self, prefix_name: &str) -> Result> where @@ -460,3 +475,22 @@ impl InnerStore for DBStorage { }) } } + +impl RawDBStorage for DBStorage { + fn raw_get_pinned_cf>( + &self, + prefix: &str, + key: K, + ) -> Result> { + let cf = self.get_cf_handle(prefix)?; + let res = self + .db + .get_pinned_cf_opt(cf, key, &ReadOptions::default())?; + Ok(res) + } + + fn raw_write_batch(&self, batch: DBWriteBatch) -> Result<()> { + self.db.write(batch)?; + Ok(()) + } +} diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 0246b6e7f4..a027b3fa57 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,41 +1,45 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::accumulator::{ - AccumulatorStorage, BlockAccumulatorStorage, TransactionAccumulatorStorage, +use crate::{ + accumulator::{AccumulatorStorage, BlockAccumulatorStorage, TransactionAccumulatorStorage}, + block::BlockStorage, + block_info::{BlockInfoStorage, BlockInfoStore}, + chain_info::ChainInfoStorage, + contract_event::ContractEventStorage, + state_node::StateStorage, + storage::{CodecKVStore, CodecWriteBatch, ColumnFamilyName, StorageInstance}, }; -use crate::block::BlockStorage; -use crate::block_info::{BlockInfoStorage, BlockInfoStore}; -use crate::chain_info::ChainInfoStorage; -use crate::contract_event::ContractEventStorage; -use crate::state_node::StateStorage; -use crate::storage::{CodecKVStore, CodecWriteBatch, ColumnFamilyName, StorageInstance}; -use crate::table_info::{TableInfoStorage, TableInfoStore}; -use crate::transaction::TransactionStorage; -use crate::transaction_info::{TransactionInfoHashStorage, TransactionInfoStorage}; -use anyhow::{bail, format_err, Error, Result}; +//use crate::table_info::{TableInfoStorage, TableInfoStore}; +use crate::{ + transaction::TransactionStorage, + transaction_info::{TransactionInfoHashStorage, TransactionInfoStorage}, +}; +use anyhow::{bail, format_err, Error, Ok, Result}; +use flexi_dag::{SyncFlexiDagSnapshot, SyncFlexiDagSnapshotStorage, SyncFlexiDagStorage}; use network_p2p_types::peer_id::PeerId; use num_enum::{IntoPrimitive, TryFromPrimitive}; use once_cell::sync::Lazy; -use starcoin_accumulator::node::AccumulatorStoreType; -use starcoin_accumulator::AccumulatorTreeStore; +use starcoin_accumulator::{ + accumulator_info::AccumulatorInfo, node::AccumulatorStoreType, AccumulatorTreeStore, +}; use starcoin_crypto::HashValue; +use starcoin_logger::prelude::info; use starcoin_state_store_api::{StateNode, StateNodeStore}; -use starcoin_types::contract_event::ContractEvent; -use starcoin_types::startup_info::{ChainInfo, ChainStatus, SnapshotRange}; -use starcoin_types::transaction::{RichTransactionInfo, Transaction}; use starcoin_types::{ block::{Block, BlockBody, BlockHeader, BlockInfo}, - startup_info::StartupInfo, + blockhash::ORIGIN, + contract_event::ContractEvent, + startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo}, + transaction::{RichTransactionInfo, Transaction}, }; //use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; -use starcoin_types::account_address::AccountAddress; -use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; -use std::collections::BTreeMap; -use std::fmt::{Debug, Display, Formatter}; -use std::sync::Arc; -pub use upgrade::BARNARD_HARD_FORK_HASH; -pub use upgrade::BARNARD_HARD_FORK_HEIGHT; +use std::{ + collections::BTreeMap, + fmt::{Debug, Display, Formatter}, + sync::Arc, +}; +pub use upgrade::{BARNARD_HARD_FORK_HASH, BARNARD_HARD_FORK_HEIGHT}; pub mod accumulator; pub mod batch; @@ -46,6 +50,7 @@ pub mod chain_info; pub mod contract_event; pub mod db_storage; pub mod errors; +pub mod flexi_dag; pub mod metrics; pub mod state_node; pub mod storage; @@ -64,6 +69,7 @@ pub const BLOCK_ACCUMULATOR_NODE_PREFIX_NAME: ColumnFamilyName = "acc_node_block pub const TRANSACTION_ACCUMULATOR_NODE_PREFIX_NAME: ColumnFamilyName = "acc_node_transaction"; pub const BLOCK_PREFIX_NAME: ColumnFamilyName = "block"; pub const BLOCK_HEADER_PREFIX_NAME: ColumnFamilyName = "block_header"; +pub const BLOCK_TIPS_HEADER_PREFIX_NAME: ColumnFamilyName = "block_tips_header"; pub const BLOCK_BODY_PREFIX_NAME: ColumnFamilyName = "block_body"; pub const BLOCK_INFO_PREFIX_NAME: ColumnFamilyName = "block_info"; pub const BLOCK_TRANSACTIONS_PREFIX_NAME: ColumnFamilyName = "block_txns"; @@ -78,6 +84,8 @@ pub const TRANSACTION_INFO_HASH_PREFIX_NAME: ColumnFamilyName = "transaction_inf pub const CONTRACT_EVENT_PREFIX_NAME: ColumnFamilyName = "contract_event"; pub const FAILED_BLOCK_PREFIX_NAME: ColumnFamilyName = "failed_block"; pub const TABLE_INFO_PREFIX_NAME: ColumnFamilyName = "table_info"; +pub const SYNC_FLEXI_DAG_ACCUMULATOR_PREFIX_NAME: ColumnFamilyName = "sync_flexi_dag_accumulator"; +pub const SYNC_FLEXI_DAG_SNAPSHOT_PREFIX_NAME: ColumnFamilyName = "sync_flexi_dag_snapshot"; ///db storage use prefix_name vec to init /// Please note that adding a prefix needs to be added in vec simultaneously, remember!! @@ -143,17 +151,43 @@ static VEC_PREFIX_NAME_V3: Lazy> = Lazy::new(|| { TABLE_INFO_PREFIX_NAME, ] }); + +static VEC_PREFIX_NAME_V4: Lazy> = Lazy::new(|| { + vec![ + BLOCK_ACCUMULATOR_NODE_PREFIX_NAME, + TRANSACTION_ACCUMULATOR_NODE_PREFIX_NAME, + BLOCK_PREFIX_NAME, + BLOCK_HEADER_PREFIX_NAME, + BLOCK_BODY_PREFIX_NAME, // unused column + BLOCK_INFO_PREFIX_NAME, + BLOCK_TRANSACTIONS_PREFIX_NAME, + BLOCK_TRANSACTION_INFOS_PREFIX_NAME, + STATE_NODE_PREFIX_NAME, + CHAIN_INFO_PREFIX_NAME, + TRANSACTION_PREFIX_NAME, + TRANSACTION_INFO_PREFIX_NAME, // unused column + TRANSACTION_INFO_PREFIX_NAME_V2, + TRANSACTION_INFO_HASH_PREFIX_NAME, + CONTRACT_EVENT_PREFIX_NAME, + FAILED_BLOCK_PREFIX_NAME, + SYNC_FLEXI_DAG_ACCUMULATOR_PREFIX_NAME, + SYNC_FLEXI_DAG_SNAPSHOT_PREFIX_NAME, + // TABLE_INFO_PREFIX_NAME, + ] +}); + #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum StorageVersion { V1 = 1, V2 = 2, V3 = 3, + V4 = 4, } impl StorageVersion { pub fn current_version() -> StorageVersion { - StorageVersion::V3 + StorageVersion::V4 } pub fn get_column_family_names(&self) -> &'static [ColumnFamilyName] { @@ -161,10 +195,18 @@ impl StorageVersion { StorageVersion::V1 => &VEC_PREFIX_NAME_V1, StorageVersion::V2 => &VEC_PREFIX_NAME_V2, StorageVersion::V3 => &VEC_PREFIX_NAME_V3, + StorageVersion::V4 => &VEC_PREFIX_NAME_V4, } } } +// pub trait DagBlockStore { +// fn get_flexi_dag_startup_info(&self) -> Result>; +// fn save_flexi_dag_startup_info(&self, startup_info: StartupInfo) -> Result<()>; +// fn get_dag_accumulator_info(&self) -> Result; +// fn get_last_tips(&self) -> Result>>; +// } + pub trait BlockStore { fn get_startup_info(&self) -> Result>; fn save_startup_info(&self, startup_info: StartupInfo) -> Result<()>; @@ -188,6 +230,11 @@ pub trait BlockStore { fn get_block_header_by_hash(&self, block_id: HashValue) -> Result>; + fn get_block_tips_header_by_hash( + &self, + block_id: HashValue, + ) -> Result>>; + fn get_block_by_hash(&self, block_id: HashValue) -> Result>; fn save_block_transaction_ids( @@ -263,6 +310,20 @@ pub trait TransactionStore { fn get_transactions(&self, txn_hash_vec: Vec) -> Result>>; } +pub trait SyncFlexiDagStore { + fn put_hashes(&self, key: HashValue, accumulator_snapshot: SyncFlexiDagSnapshot) -> Result<()>; + fn query_by_hash(&self, key: HashValue) -> Result>; + fn get_accumulator_snapshot_storage(&self) -> std::sync::Arc; + fn append_dag_accumulator_leaf( + &self, + key: HashValue, + new_tips: Vec, + accumulator_info: AccumulatorInfo, + ) -> Result<()>; + fn get_dag_accumulator_info(&self, block_id: HashValue) -> Result>; + fn get_tips_by_block_id(&self, block_id: HashValue) -> Result>; +} + // TODO: remove Arc, we can clone Storage directly. #[derive(Clone)] pub struct Storage { @@ -276,6 +337,7 @@ pub struct Storage { block_info_storage: BlockInfoStorage, event_storage: ContractEventStorage, chain_info_storage: ChainInfoStorage, + flexi_dag_storage: SyncFlexiDagStorage, table_info_storage: TableInfoStorage, // instance: StorageInstance, } @@ -296,6 +358,7 @@ impl Storage { block_info_storage: BlockInfoStorage::new(instance.clone()), event_storage: ContractEventStorage::new(instance.clone()), chain_info_storage: ChainInfoStorage::new(instance.clone()), + flexi_dag_storage: SyncFlexiDagStorage::new(instance), table_info_storage: TableInfoStorage::new(instance), // instance, }; @@ -376,11 +439,21 @@ impl BlockStore for Storage { let head_block_info = self.get_block_info(head_block.id())?.ok_or_else(|| { format_err!("Startup block info {:?} should exist", startup_info.main) })?; - Ok(Some(ChainInfo::new( + + let flexi_dag_accumulator_info = self + .get_dag_accumulator_info(head_block.id()) + .unwrap_or(Some(AccumulatorInfo::default())) + .unwrap(); + let tips_header_hash = self + .get_tips_by_block_id(head_block.id()) + .unwrap_or(vec![genesis_hash]); + let chain_info = ChainInfo::new( head_block.chain_id(), genesis_hash, - ChainStatus::new(head_block, head_block_info), - ))) + ChainStatus::new(head_block.clone(), head_block_info, Some(tips_header_hash)), + Some(flexi_dag_accumulator_info), + ); + Ok(Some(chain_info)) } fn get_block(&self, block_id: HashValue) -> Result> { @@ -408,6 +481,13 @@ impl BlockStore for Storage { self.block_storage.get_block_header_by_hash(block_id) } + fn get_block_tips_header_by_hash( + &self, + block_id: HashValue, + ) -> Result>> { + self.block_storage.get_block_tips_header_by_hash(block_id) + } + fn get_block_by_hash(&self, block_id: HashValue) -> Result> { self.block_storage.get_block_by_hash(block_id) } @@ -571,9 +651,80 @@ impl TransactionStore for Storage { } } +impl SyncFlexiDagStore for Storage { + fn put_hashes(&self, key: HashValue, accumulator_snapshot: SyncFlexiDagSnapshot) -> Result<()> { + self.flexi_dag_storage.put_hashes(key, accumulator_snapshot) + } + + fn query_by_hash(&self, key: HashValue) -> Result> { + self.flexi_dag_storage.get_hashes_by_hash(key) + } + + fn get_accumulator_snapshot_storage(&self) -> std::sync::Arc { + self.flexi_dag_storage.get_snapshot_storage() + } + + fn get_dag_accumulator_info(&self, block_id: HashValue) -> Result> { + match self + .flexi_dag_storage + .get_snapshot_storage() + .get(block_id)? + { + Some(snapshot) => Ok(Some(snapshot.accumulator_info)), + None => Ok(None), + } + } + + fn append_dag_accumulator_leaf( + &self, + key: HashValue, + new_tips: Vec, + accumulator_info: AccumulatorInfo, + ) -> Result<()> { + let snapshot = SyncFlexiDagSnapshot { + child_hashes: new_tips.clone(), + accumulator_info: accumulator_info.clone(), + }; + // for sync + if self.flexi_dag_storage.get_hashes_by_hash(key)?.is_some() { + if let Some(t) = self.flexi_dag_storage.get_hashes_by_hash(key)? { + if t != snapshot { + bail!("the accumulator differ from other"); + } + } + } + self.flexi_dag_storage.put_hashes(key, snapshot.clone())?; + + // for block chain + new_tips.iter().try_fold((), |_, block_id| { + if let Some(t) = self + .flexi_dag_storage + .get_hashes_by_hash(block_id.clone())? + { + if t != snapshot { + bail!("the key {} should not exists", block_id); + } + } + self.flexi_dag_storage + .put_hashes(block_id.clone(), snapshot.clone()) + })?; + Ok(()) + } + + fn get_tips_by_block_id(&self, block_id: HashValue) -> Result> { + match self.query_by_hash(block_id)? { + Some(snapshot) => Ok(snapshot.child_hashes), + None => { + bail!("failed to get snapshot by hash: {}", block_id); + } + } + } +} + /// Chain storage define pub trait Store: StateNodeStore + + SyncFlexiDagStore + BlockStore + BlockInfoStore + TransactionStore @@ -653,6 +804,9 @@ impl Store for Storage { AccumulatorStoreType::Transaction => { Arc::new(self.transaction_accumulator_storage.clone()) } + AccumulatorStoreType::SyncDag => { + Arc::new(self.flexi_dag_storage.get_accumulator_storage()) + } } } } diff --git a/storage/src/storage.rs b/storage/src/storage.rs index cddd7269b1..7cc4fe1abe 100644 --- a/storage/src/storage.rs +++ b/storage/src/storage.rs @@ -2,19 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 pub use crate::batch::WriteBatch; -use crate::cache_storage::CacheStorage; -use crate::db_storage::{DBStorage, SchemaIterator}; -use crate::upgrade::DBUpgrade; +use crate::{ + cache_storage::CacheStorage, + db_storage::{DBStorage, SchemaIterator}, + upgrade::DBUpgrade, +}; use anyhow::{bail, format_err, Result}; use byteorder::{BigEndian, ReadBytesExt}; +use rocksdb::{DBPinnableSlice, WriteBatch as DBWriteBatch}; use starcoin_config::NodeConfig; use starcoin_crypto::HashValue; use starcoin_logger::prelude::info; use starcoin_vm_types::state_store::table::TableHandle; -use std::convert::TryInto; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::sync::Arc; +use std::{convert::TryInto, fmt::Debug, marker::PhantomData, sync::Arc}; /// Type alias to improve readability. pub type ColumnFamilyName = &'static str; @@ -46,6 +46,16 @@ pub trait InnerStore: Send + Sync { fn multi_get(&self, prefix_name: &str, keys: Vec>) -> Result>>>; } +pub trait RawDBStorage: Send + Sync { + fn raw_get_pinned_cf>( + &self, + prefix: &str, + key: K, + ) -> Result>; + + fn raw_write_batch(&self, batch: DBWriteBatch) -> Result<()>; +} + ///Storage instance type define #[derive(Clone)] #[allow(clippy::upper_case_acronyms)] diff --git a/storage/src/tests/mod.rs b/storage/src/tests/mod.rs index abf8a51ed0..7c781ecc4e 100644 --- a/storage/src/tests/mod.rs +++ b/storage/src/tests/mod.rs @@ -3,4 +3,5 @@ mod test_accumulator; mod test_batch; mod test_block; +mod test_dag; mod test_storage; diff --git a/storage/src/tests/test_storage.rs b/storage/src/tests/test_storage.rs index be7a2eaa44..90e2cdecff 100644 --- a/storage/src/tests/test_storage.rs +++ b/storage/src/tests/test_storage.rs @@ -18,15 +18,15 @@ use anyhow::Result; use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_config::RocksdbConfig; use starcoin_crypto::HashValue; -use starcoin_types::{ - account_address::AccountAddress, - block::{Block, BlockBody, BlockHeader, BlockInfo}, - language_storage::TypeTag, - startup_info::SnapshotRange, - transaction::{RichTransactionInfo, SignedUserTransaction, Transaction, TransactionInfo}, - vm_error::KeptVMStatus, +use starcoin_types::block::{Block, BlockBody, BlockHeader, BlockInfo}; +//use starcoin_types::language_storage::TypeTag; +use starcoin_types::startup_info::SnapshotRange; +use starcoin_types::transaction::{ + RichTransactionInfo, SignedUserTransaction, Transaction, TransactionInfo, }; -use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; +use starcoin_types::vm_error::KeptVMStatus; +//use starcoin_vm_types::account_address::AccountAddress; +//use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use std::path::Path; #[test] @@ -296,7 +296,7 @@ fn generate_old_db(path: &Path) -> Result> { BlockBody::new(vec![txn.clone()], None), ); let mut txn_inf_ids = vec![]; - let block_metadata = block.to_metadata(0); + let block_metadata = block.to_metadata(0, None); let txn_info_0 = TransactionInfo::new( block_metadata.id(), HashValue::random(), diff --git a/storage/src/upgrade.rs b/storage/src/upgrade.rs index b8fcd18b43..da6b9e15d6 100644 --- a/storage/src/upgrade.rs +++ b/storage/src/upgrade.rs @@ -163,6 +163,11 @@ impl DBUpgrade { Ok(()) } + fn db_upgrade_v3_v4(_instance: &mut StorageInstance) -> Result<()> { + // https://github.com/facebook/rocksdb/issues/1295 + Ok(()) + } + pub fn do_upgrade( version_in_db: StorageVersion, version_in_code: StorageVersion, @@ -185,6 +190,12 @@ impl DBUpgrade { (StorageVersion::V2, StorageVersion::V3) => { Self::db_upgrade_v2_v3(instance)?; } + (StorageVersion::V3, StorageVersion::V4) + | (StorageVersion::V1, StorageVersion::V4) + | (StorageVersion::V2, StorageVersion::V4) => { + // just for testing. todo + Self::db_upgrade_v3_v4(instance)?; + } _ => bail!( "Can not upgrade db from {:?} to {:?}", version_in_db, diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 70c429f5cf..256457897d 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -42,6 +42,7 @@ stest = { workspace = true } stream-task = { workspace = true } sysinfo = { workspace = true } thiserror = { workspace = true } +starcoin-consensus = { workspace = true } timeout-join-handler = { workspace = true } [dev-dependencies] @@ -58,6 +59,7 @@ starcoin-txpool-mock-service = { workspace = true } starcoin-executor = { workspace = true } test-helper = { workspace = true } tokio = { features = ["full"], workspace = true } +starcoin-genesis = { workspace = true } [package] authors = { workspace = true } diff --git a/sync/api/src/lib.rs b/sync/api/src/lib.rs index 284bbce588..d541abfa25 100644 --- a/sync/api/src/lib.rs +++ b/sync/api/src/lib.rs @@ -1,6 +1,9 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +use std::any; +use std::sync::Arc; + use anyhow::Result; use network_api::PeerId; use network_api::PeerStrategy; @@ -9,6 +12,7 @@ use serde::{Deserialize, Serialize}; pub use service::{SyncAsyncService, SyncServiceHandler}; use starcoin_crypto::HashValue; use starcoin_service_registry::ServiceRequest; +use starcoin_types::block::ExecutedBlock; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use starcoin_types::sync_status::SyncStatus; use starcoin_types::U256; @@ -23,11 +27,16 @@ pub struct StartSyncTxnEvent; pub struct PeerNewBlock { peer_id: PeerId, new_block: Block, + dag_parents: Option>, } impl PeerNewBlock { - pub fn new(peer_id: PeerId, new_block: Block) -> Self { - PeerNewBlock { peer_id, new_block } + pub fn new(peer_id: PeerId, new_block: Block, dag_parents: Option>) -> Self { + PeerNewBlock { + peer_id, + new_block, + dag_parents, + } } pub fn get_peer_id(&self) -> PeerId { @@ -37,6 +46,10 @@ impl PeerNewBlock { pub fn get_block(&self) -> &Block { &self.new_block } + + pub fn get_dag_parents(&self) -> &Option> { + &self.dag_parents + } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index 11e1addffa..1f9b209a96 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -8,10 +8,11 @@ use crate::sync::{CheckSyncEvent, SyncService}; use crate::tasks::{BlockConnectedEvent, BlockConnectedFinishEvent, BlockDiskCheckEvent}; #[cfg(test)] use anyhow::bail; -use anyhow::{format_err, Result}; +use anyhow::{format_err, Ok, Result}; use network_api::PeerProvider; use starcoin_chain_api::{ChainReader, ConnectBlockError, WriteableChainService}; use starcoin_config::{NodeConfig, G_CRATE_VERSION}; +use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_logger::prelude::*; @@ -28,7 +29,7 @@ use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::ExecutedBlock; use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::{MinedBlock, SyncStatusChangeEvent, SystemShutdown}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use sysinfo::{DiskExt, System, SystemExt}; const DISK_CHECKPOINT_FOR_PANIC: u64 = 1024 * 1024 * 1024 * 3; @@ -131,6 +132,7 @@ where .get_startup_info()? .ok_or_else(|| format_err!("Startup info should exist."))?; let vm_metrics = ctx.get_shared_opt::()?; + let dag = ctx.get_shared::>>()?; let chain_service = WriteBlockChainService::new( config.clone(), startup_info, @@ -138,6 +140,7 @@ where txpool, bus, vm_metrics, + dag.clone(), )?; Ok(Self::new(chain_service, config)) @@ -180,7 +183,7 @@ where ) { if let Some(res) = self.check_disk_space() { match res { - Ok(available_space) => { + std::result::Result::Ok(available_space) => { warn!("Available diskspace only {}/GB left ", available_space) } Err(e) => { @@ -206,7 +209,7 @@ impl EventHandler for BlockConnectorService { - if let Err(e) = self.chain_service.try_connect(block) { + if let Err(e) = self.chain_service.try_connect(block, msg.dag_parents) { error!("Process connected new block from sync error: {:?}", e); } } @@ -257,12 +260,15 @@ where TransactionPoolServiceT: TxPoolSyncService + 'static, { fn handle_event(&mut self, msg: MinedBlock, _ctx: &mut ServiceContext) { - let MinedBlock(new_block) = msg; + let MinedBlock(new_block, tips_headers) = msg; let id = new_block.header().id(); debug!("try connect mined block: {}", id); - match self.chain_service.try_connect(new_block.as_ref().clone()) { - Ok(_) => debug!("Process mined block {} success.", id), + match self + .chain_service + .try_connect(new_block.as_ref().clone(), tips_headers) + { + std::result::Result::Ok(_) => debug!("Process mined block {} success.", id), Err(e) => { warn!("Process mined block {} fail, error: {:?}", id, e); } @@ -291,13 +297,18 @@ where return; } let peer_id = msg.get_peer_id(); - if let Err(e) = self.chain_service.try_connect(msg.get_block().clone()) { + if let Err(e) = self + .chain_service + .try_connect(msg.get_block().clone(), msg.get_dag_parents().clone()) + { match e.downcast::() { - Ok(connect_error) => { + std::result::Result::Ok(connect_error) => { match connect_error { ConnectBlockError::FutureBlock(block) => { //TODO cache future block - if let Ok(sync_service) = ctx.service_ref::() { + if let std::result::Result::Ok(sync_service) = + ctx.service_ref::() + { info!( "BlockConnector try connect future block ({:?},{}), peer_id:{:?}, notify Sync service check sync.", block.id(), @@ -353,7 +364,8 @@ where msg: ResetRequest, _ctx: &mut ServiceContext>, ) -> Result<()> { - self.chain_service.reset(msg.block_hash) + self.chain_service + .reset(msg.block_hash, msg.dag_block_parent) } } @@ -367,7 +379,8 @@ where msg: ExecuteRequest, _ctx: &mut ServiceContext>, ) -> Result { - self.chain_service.execute(msg.block) + self.chain_service + .execute(msg.block, msg.dag_transaction_parent) } } diff --git a/sync/src/block_connector/mod.rs b/sync/src/block_connector/mod.rs index 72ea8d3560..3e69c86527 100644 --- a/sync/src/block_connector/mod.rs +++ b/sync/src/block_connector/mod.rs @@ -11,6 +11,8 @@ mod metrics; mod test_illegal_block; #[cfg(test)] mod test_write_block_chain; +#[cfg(test)] +mod test_write_dag_block_chain; mod write_block_chain; pub use block_connector_service::BlockConnectorService; @@ -26,6 +28,7 @@ pub use test_write_block_chain::new_block; #[derive(Debug, Clone)] pub struct ResetRequest { pub block_hash: HashValue, + pub dag_block_parent: Option>, } impl ServiceRequest for ResetRequest { @@ -35,6 +38,8 @@ impl ServiceRequest for ResetRequest { #[derive(Debug, Clone)] pub struct ExecuteRequest { pub block: Block, + pub dag_block_parent: Option>, + pub dag_transaction_parent: Option, } impl ServiceRequest for ExecuteRequest { diff --git a/sync/src/block_connector/test_illegal_block.rs b/sync/src/block_connector/test_illegal_block.rs index ec2b662895..808d4d04dc 100644 --- a/sync/src/block_connector/test_illegal_block.rs +++ b/sync/src/block_connector/test_illegal_block.rs @@ -123,7 +123,7 @@ fn apply_with_illegal_uncle( .current_header() .id(); let mut main = BlockChain::new(net.time_service(), head_id, storage, None)?; - main.apply(new_block.clone())?; + main.apply(new_block.clone(), None, &mut None)?; Ok(new_block) } @@ -147,7 +147,7 @@ fn apply_legal_block( ) .unwrap(); writeable_block_chain_service - .try_connect(new_block) + .try_connect(new_block, None) .unwrap(); } @@ -160,7 +160,7 @@ async fn test_verify_gas_limit(succ: bool) -> Result<()> { .with_gas_used(u64::MAX) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -183,7 +183,7 @@ async fn test_verify_body_hash(succ: bool) -> Result<()> { .with_body_hash(HashValue::random()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -206,7 +206,7 @@ async fn test_verify_parent_id(succ: bool) -> Result<()> { .with_parent_hash(HashValue::random()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -234,7 +234,7 @@ async fn test_verify_timestamp(succ: bool) -> Result<()> { .with_timestamp(main.current_header().timestamp()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -263,7 +263,7 @@ async fn test_verify_future_timestamp(succ: bool) -> Result<()> { ) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -286,7 +286,7 @@ async fn test_verify_consensus(succ: bool) -> Result<()> { .with_difficulty(U256::from(1024u64)) .build(); } - main.apply(new_block)?; + main.apply(new_block, None)?; Ok(()) } @@ -371,7 +371,7 @@ async fn test_verify_can_not_be_uncle_check_ancestor_failed() { .consensus() .create_block(block_template, net.time_service().as_ref()) .unwrap(); - new_branch.apply(new_block).unwrap(); + new_branch.apply(new_block, None, &mut None).unwrap(); } // 3. new block @@ -468,7 +468,7 @@ async fn test_verify_illegal_uncle_consensus(succ: bool) -> Result<()> { .create_block(block_template, net.time_service().as_ref()) .unwrap(); - main_block_chain.apply(new_block)?; + main_block_chain.apply(new_block, None, &mut None)?; Ok(()) } @@ -491,7 +491,7 @@ async fn test_verify_state_root(succ: bool) -> Result<()> { .with_state_root(HashValue::random()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -510,7 +510,7 @@ async fn test_verify_block_used_gas(succ: bool) -> Result<()> { if !succ { new_block.header = new_block.header().as_builder().with_gas_used(1).build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -532,7 +532,7 @@ async fn test_verify_txn_count_failed() { let mut body = new_block.body.clone(); body.transactions = txns; new_block.body = body; - let apply_failed = main.apply(new_block); + let apply_failed = main.apply(new_block, None, &mut None); assert!(apply_failed.is_err()); if let Err(apply_err) = apply_failed { error!("apply failed : {:?}", apply_err); @@ -548,7 +548,7 @@ async fn test_verify_accumulator_root(succ: bool) -> Result<()> { .with_accumulator_root(HashValue::random()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -571,7 +571,7 @@ async fn test_verify_block_accumulator_root(succ: bool) -> Result<()> { .with_parent_block_accumulator_root(HashValue::random()) .build(); } - main.apply(new_block)?; + main.apply(new_block, None, &mut None)?; Ok(()) } @@ -602,7 +602,7 @@ async fn test_verify_block_number_failed(succ: bool, order: bool) { .build(); } } - let apply_failed = main.apply(new_block); + let apply_failed = main.apply(new_block, None, &mut None); if !succ { assert!(apply_failed.is_err()); if let Err(apply_err) = apply_failed { @@ -768,7 +768,7 @@ async fn test_verify_uncles_uncle_exist_failed() { .create_block(block_template, net.time_service().as_ref()) .unwrap(); writeable_block_chain_service - .try_connect(new_block) + .try_connect(new_block, None) .unwrap(); info!( @@ -838,7 +838,7 @@ async fn test_verify_uncle_and_parent_number_failed() { .create_block(block_template, net.time_service().as_ref()) .unwrap(); writeable_block_chain_service - .try_connect(new_block) + .try_connect(new_block, None) .unwrap(); let new_number = writeable_block_chain_service .get_main() diff --git a/sync/src/block_connector/test_write_block_chain.rs b/sync/src/block_connector/test_write_block_chain.rs index c94ebe91b9..95da34b774 100644 --- a/sync/src/block_connector/test_write_block_chain.rs +++ b/sync/src/block_connector/test_write_block_chain.rs @@ -7,6 +7,8 @@ use starcoin_chain::{BlockChain, ChainReader}; use starcoin_chain_service::WriteableChainService; use starcoin_config::NodeConfig; use starcoin_consensus::Consensus; +use starcoin_consensus::{BlockDAG, Consensus, FlexiDagStorage, FlexiDagStorageConfig}; +use starcoin_crypto::HashValue; use starcoin_genesis::Genesis as StarcoinGenesis; use starcoin_service_registry::bus::BusService; use starcoin_service_registry::{RegistryAsyncService, RegistryService}; @@ -14,8 +16,10 @@ use starcoin_storage::Store; use starcoin_time_service::TimeService; use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::Block; +use starcoin_types::blockhash::ORIGIN; +use starcoin_types::header::Header; use starcoin_types::startup_info::StartupInfo; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; pub async fn create_writeable_block_chain() -> ( WriteBlockChainService, @@ -25,11 +29,34 @@ pub async fn create_writeable_block_chain() -> ( let node_config = NodeConfig::random_for_test(); let node_config = Arc::new(node_config); - let (storage, chain_info, _) = StarcoinGenesis::init_storage_for_test(node_config.net()) + let (storage, chain_info, genesis) = StarcoinGenesis::init_storage_for_test(node_config.net()) .expect("init storage by genesis fail."); let registry = RegistryService::launch(); let bus = registry.service_ref::().await.unwrap(); let txpool_service = MockTxPoolService::new(); + + genesis.save(node_config.data_dir()).unwrap(); + + let (chain_info, genesis) = StarcoinGenesis::init_and_check_storage( + node_config.net(), + storage.clone(), + node_config.data_dir(), + ) + .expect("init chain and genesis error"); + + let flex_dag_config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); + let flex_dag_db = FlexiDagStorage::create_from_path("./smolstc", flex_dag_config) + .expect("Failed to create flexidag storage"); + + let dag = BlockDAG::new( + Header::new( + genesis.block().header().clone(), + vec![HashValue::new(ORIGIN)], + ), + 3, + flex_dag_db, + ); + ( WriteBlockChainService::new( node_config.clone(), @@ -38,6 +65,7 @@ pub async fn create_writeable_block_chain() -> ( txpool_service, bus, None, + Arc::new(Mutex::new(dag)), ) .unwrap(), node_config, @@ -58,7 +86,8 @@ pub fn gen_blocks( writeable_block_chain_service, time_service, ); - writeable_block_chain_service.try_connect(block).unwrap(); + let e = writeable_block_chain_service.try_connect(block, None); + println!("try_connect result: {:?}", e) } } } @@ -79,7 +108,7 @@ pub fn new_block( .unwrap(); block_chain .consensus() - .create_block(block_template, time_service) + .create_single_chain_block(block_template, time_service) .unwrap() } @@ -100,6 +129,7 @@ async fn test_block_chain_apply() { .number(), times ); + println!("finish test_block_chain_apply"); } fn gen_fork_block_chain( @@ -129,11 +159,13 @@ fn gen_fork_block_chain( .unwrap(); let block = block_chain .consensus() - .create_block(block_template, net.time_service().as_ref()) + .create_single_chain_block(block_template, net.time_service().as_ref()) .unwrap(); parent_id = block.id(); - writeable_block_chain_service.try_connect(block).unwrap(); + writeable_block_chain_service + .try_connect(block, None) + .unwrap(); } } } @@ -227,7 +259,7 @@ async fn test_block_chain_reset() -> anyhow::Result<()> { .get_main() .get_block_by_number(3)? .unwrap(); - writeable_block_chain_service.reset(block.id())?; + writeable_block_chain_service.reset(block.id(), None)?; assert_eq!( writeable_block_chain_service .get_main() diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index cab825e583..732d278d6e 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -2,25 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 use crate::block_connector::metrics::ChainMetrics; -use anyhow::{format_err, Result}; +use anyhow::{bail, format_err, Ok, Result}; +use async_std::stream::StreamExt; use starcoin_chain::BlockChain; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, WriteableChainService}; use starcoin_config::NodeConfig; +use starcoin_consensus::dag::ghostdag::protocol::ColoringOutput; +use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_logger::prelude::*; use starcoin_service_registry::bus::{Bus, BusService}; use starcoin_service_registry::{ServiceContext, ServiceRef}; +use starcoin_storage::storage::CodecKVStore; use starcoin_storage::Store; +use starcoin_time_service::{DagBlockTimeWindowService, TimeWindowResult}; use starcoin_txpool_api::TxPoolSyncService; use starcoin_types::block::BlockInfo; +use starcoin_types::blockhash::BlockHashMap; use starcoin_types::{ block::{Block, BlockHeader, ExecutedBlock}, startup_info::StartupInfo, system_events::{NewBranch, NewHeadBlock}, }; use std::fmt::Formatter; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use super::BlockConnectorService; @@ -38,26 +44,50 @@ where bus: ServiceRef, metrics: Option, vm_metrics: Option, + dag_block_pool: Arc)>>>, + dag: Arc>, } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub enum ConnectOk { - Duplicate, + Duplicate(ExecutedBlock), //Execute block and connect to main - ExeConnectMain, + ExeConnectMain(ExecutedBlock), //Execute block and connect to branch. - ExeConnectBranch, + ExeConnectBranch(ExecutedBlock), //Block has executed, just connect. - Connect, + Connect(ExecutedBlock), + + //Block has executed, just connect. + DagConnected, + // the retry block + MainDuplicate, + // the dag block waiting for the time window end + DagPending, +} + +impl ConnectOk { + pub fn block(&self) -> Option { + match self { + ConnectOk::Duplicate(block) => Some(block.clone()), + ConnectOk::ExeConnectMain(block) => Some(block.clone()), + ConnectOk::ExeConnectBranch(block) => Some(block.clone()), + ConnectOk::Connect(block) => Some(block.clone()), + ConnectOk::DagConnected | ConnectOk::MainDuplicate | ConnectOk::DagPending => None, + } + } } impl std::fmt::Display for ConnectOk { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s = match self { - ConnectOk::Duplicate => "Duplicate", - ConnectOk::ExeConnectMain => "ExeConnectMain", - ConnectOk::ExeConnectBranch => "ExeConnectBranch", - ConnectOk::Connect => "Connect", + ConnectOk::Duplicate(_) => "Duplicate", + ConnectOk::ExeConnectMain(_) => "ExeConnectMain", + ConnectOk::ExeConnectBranch(_) => "ExeConnectBranch", + ConnectOk::Connect(_) => "Connect", + ConnectOk::DagConnected => "DagConnect", + ConnectOk::MainDuplicate => "MainDuplicate", + ConnectOk::DagPending => "DagPending", }; write!(f, "{}", s) } @@ -67,17 +97,17 @@ impl

WriteableChainService for WriteBlockChainService

where P: TxPoolSyncService + 'static, { - fn try_connect(&mut self, block: Block) -> Result<()> { + fn try_connect(&mut self, block: Block, tips_headers: Option>) -> Result<()> { let _timer = self .metrics .as_ref() .map(|metrics| metrics.chain_block_connect_time.start_timer()); - let result = self.connect_inner(block); + let result = self.connect_inner(block, tips_headers); if let Some(metrics) = self.metrics.as_ref() { let result = match result.as_ref() { - Ok(connect) => format!("Ok_{}", connect), + std::result::Result::Ok(connect) => format!("Ok_{}", connect), Err(err) => { if let Some(connect_err) = err.downcast_ref::() { format!("Err_{}", connect_err.reason()) @@ -106,6 +136,7 @@ where txpool: TransactionPoolServiceT, bus: ServiceRef, vm_metrics: Option, + dag: Arc>, ) -> Result { let net = config.net(); let main = BlockChain::new( @@ -128,17 +159,21 @@ where bus, metrics, vm_metrics, + dag_block_pool: Arc::new(Mutex::new(vec![])), + dag, }) } fn find_or_fork( &self, header: &BlockHeader, + dag_block_next_parent: Option, + dag_block_parents: Option>, ) -> Result<(Option, Option)> { let block_id = header.id(); let block_info = self.storage.get_block_info(block_id)?; let block_chain = if block_info.is_some() { - if self.is_main_head(&header.parent_hash()) { + if self.is_main_head(&header.parent_hash(), dag_block_parents) { None } else { let net = self.config.net(); @@ -149,11 +184,11 @@ where self.vm_metrics.clone(), )?) } - } else if self.block_exist(header.parent_hash())? { + } else if self.block_exist(header.parent_hash())? || self.blocks_exist(dag_block_parents)? { let net = self.config.net(); Some(BlockChain::new( net.time_service(), - header.parent_hash(), + dag_block_next_parent.unwrap_or(header.parent_hash()), self.storage.clone(), self.vm_metrics.clone(), )?) @@ -167,10 +202,25 @@ where Ok(matches!(self.storage.get_block_info(block_id)?, Some(_))) } + fn blocks_exist(&self, block_id: Option>) -> Result { + if let Some(block_id) = block_id { + if block_id.is_empty() { + return Ok(false); + } + return Ok(matches!(self.storage.get_block_infos(block_id)?, _)); + } + return Ok(false); + } + pub fn get_main(&self) -> &BlockChain { &self.main } + #[cfg(test)] + pub fn time_sleep(&self, sec: u64) { + self.config.net().time_service().sleep(sec * 1000000); + } + #[cfg(test)] pub fn apply_failed(&mut self, block: Block) -> Result<()> { use anyhow::bail; @@ -202,23 +252,56 @@ where self.vm_metrics.clone(), )?; - self.select_head(new_branch)?; + let main_total_difficulty = self.main.get_total_difficulty()?; + let branch_total_difficulty = new_branch.get_total_difficulty()?; + if branch_total_difficulty > main_total_difficulty { + self.update_startup_info(new_branch.head_block().header())?; - Ok(()) + let dag_parents = self.dag.lock().unwrap().get_parents(new_head_block)?; + let next_tips = self + .storage + .get_accumulator_snapshot_storage() + .get(new_head_block)? + .expect("the snapshot must exists!") + .child_hashes; + + ctx.broadcast(NewHeadBlock( + Arc::new(new_branch.head_block()), + Some(dag_parents), + Some(next_tips), + )); + + Ok(()) + } else { + bail!("no need to switch"); + } } - pub fn select_head(&mut self, new_branch: BlockChain) -> Result<()> { + pub fn select_head( + &mut self, + new_branch: BlockChain, + dag_block_parents: Option>, + ) -> Result<()> { let executed_block = new_branch.head_block(); let main_total_difficulty = self.main.get_total_difficulty()?; let branch_total_difficulty = new_branch.get_total_difficulty()?; - let parent_is_main_head = self.is_main_head(&executed_block.header().parent_hash()); + let parent_is_main_head = self.is_main_head( + &executed_block.header().parent_hash(), + dag_block_parents.clone(), + ); if branch_total_difficulty > main_total_difficulty { let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = - if !parent_is_main_head { - self.find_ancestors_from_accumulator(&new_branch)? + if dag_block_parents.is_some() { + // for dag + self.find_ancestors_from_dag_accumulator(&new_branch)? } else { - (1, vec![executed_block.block.clone()], 0, vec![]) + // for single chain + if !parent_is_main_head { + self.find_ancestors_from_accumulator(&new_branch)? + } else { + (1, vec![executed_block.block.clone()], 0, vec![]) + } }; self.main = new_branch; @@ -231,7 +314,7 @@ where )?; } else { //send new branch event - self.broadcast_new_branch(executed_block); + self.broadcast_new_branch(executed_block, dag_block_parents); } Ok(()) } @@ -244,8 +327,21 @@ where retracted_count: u64, retracted_blocks: Vec, ) -> Result<()> { + if enacted_blocks.is_empty() { + error!("enacted_blocks is empty."); + bail!("enacted_blocks is empty."); + } + if enacted_blocks.last().unwrap().header != executed_block.block().header { + error!("enacted_blocks.last().unwrap().header: {:?}, executed_block.block().header: {:?} are different!", + enacted_blocks.last().unwrap().header, executed_block.block().header); + bail!("enacted_blocks.last().unwrap().header: {:?}, executed_block.block().header: {:?} are different!", + enacted_blocks.last().unwrap().header, executed_block.block().header); + } debug_assert!(!enacted_blocks.is_empty()); - debug_assert_eq!(enacted_blocks.last().unwrap(), executed_block.block()); + debug_assert_eq!( + enacted_blocks.last().unwrap().header, + executed_block.block().header + ); self.update_startup_info(executed_block.block().header())?; if retracted_count > 0 { if let Some(metrics) = self.metrics.as_ref() { @@ -269,13 +365,19 @@ where .set(executed_block.block_info.txn_accumulator_info.num_leaves); } - self.broadcast_new_head(executed_block); + // self.broadcast_new_head(executed_block, dag_parents, next_tips); Ok(()) } + pub fn do_new_head_with_broadcast() {} + /// Reset the node to `block_id`, and replay blocks after the block - pub fn reset(&mut self, block_id: HashValue) -> Result<()> { + pub fn reset( + &mut self, + block_id: HashValue, + dag_block_parents: Option>, + ) -> Result<()> { let new_head_block = self .main .get_block(block_id)? @@ -306,17 +408,28 @@ where let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = (1, vec![executed_block.block.clone()], 0, vec![]); self.do_new_head( - executed_block, + executed_block.clone(), enacted_count, enacted_blocks, retracted_count, retracted_blocks, )?; + + let next_tips = self + .storage + .get_tips_by_block_id(executed_block.block.header().id()) + .ok(); + self.broadcast_new_head(executed_block, dag_block_parents, next_tips); + Ok(()) } ///Directly execute the block and save result, do not try to connect. - pub fn execute(&mut self, block: Block) -> Result { + pub fn execute( + &mut self, + block: Block, + dag_block_parent: Option, + ) -> Result { let chain = BlockChain::new( self.config.net().time_service(), block.header().parent_hash(), @@ -324,11 +437,31 @@ where self.vm_metrics.clone(), )?; let verify_block = chain.verify(block)?; - chain.execute(verify_block) + chain.execute(verify_block, dag_block_parent) } - fn is_main_head(&self, parent_id: &HashValue) -> bool { - parent_id == &self.startup_info.main + fn is_main_head( + &self, + parent_id: &HashValue, + dag_block_parents: Option>, + ) -> bool { + if parent_id == &self.startup_info.main { + return true; + } + + if let Some(block_parents) = dag_block_parents { + if self.main.status().tips_hash.is_some() && !block_parents.is_empty() { + return block_parents.into_iter().all(|block_header| { + self.main + .status() + .tips_hash + .unwrap() + .contains(&block_header) + }); + } + } + + return false; } fn update_startup_info(&mut self, main_head: &BlockHeader) -> Result<()> { @@ -342,6 +475,105 @@ where } } + fn find_ancestors_from_dag_accumulator( + &self, + new_branch: &BlockChain, + ) -> Result<(u64, Vec, u64, Vec)> { + let mut min_leaf_index = std::cmp::min( + self.main.get_dag_current_leaf_number()?, + new_branch.get_dag_current_leaf_number()?, + ) - 1; + + let mut retracted = vec![]; + let mut enacted = vec![]; + + let snapshot = + new_branch.get_dag_accumulator_snapshot(new_branch.head_block().header().id())?; + let mut children = snapshot.child_hashes.clone(); + children.sort(); + for child in children { + match self.storage.get_block(child)? { + Some(block) => enacted.push(block), + None => bail!( + "the block{} dose not exist in new branch, ignore", + child.clone() + ), + } + } + enacted.reverse(); + + loop { + if min_leaf_index == 0 { + break; + } + let main_snapshot = self + .main + .get_dag_accumulator_snapshot_by_index(min_leaf_index)?; + let new_branch_snapshot = + new_branch.get_dag_accumulator_snapshot_by_index(min_leaf_index)?; + + if main_snapshot.accumulator_info.get_accumulator_root() + == new_branch_snapshot.accumulator_info.get_accumulator_root() + { + break; + } + + let mut temp_retracted = vec![]; + temp_retracted.extend( + main_snapshot + .child_hashes + .iter() + .try_fold(Vec::::new(), |mut rollback_blocks, child| { + let block = self.storage.get_block(child.clone()); + if let anyhow::Result::Ok(Some(block)) = block { + rollback_blocks.push(block); + } else { + bail!( + "the block{} dose not exist in main branch, ignore", + child.clone() + ); + } + return Ok(rollback_blocks); + })? + .into_iter(), + ); + temp_retracted.sort_by(|a, b| b.header().id().cmp(&a.header().id())); + retracted.extend(temp_retracted.into_iter()); + + let mut temp_enacted = vec![]; + temp_enacted.extend( + new_branch_snapshot + .child_hashes + .iter() + .try_fold(Vec::::new(), |mut rollback_blocks, child| { + let block = self.storage.get_block(child.clone()); + if let anyhow::Result::Ok(Some(block)) = block { + rollback_blocks.push(block); + } else { + bail!( + "the block{} dose not exist in new branch, ignore", + child.clone() + ); + } + return Ok(rollback_blocks); + })? + .into_iter(), + ); + temp_enacted.sort_by(|a, b| b.header().id().cmp(&a.header().id())); + enacted.extend(temp_enacted.into_iter()); + + min_leaf_index = min_leaf_index.saturating_sub(1); + } + enacted.reverse(); + retracted.reverse(); + Ok(( + enacted.len() as u64, + enacted, + retracted.len() as u64, + retracted, + )) + } + fn find_ancestors_from_accumulator( &self, new_branch: &BlockChain, @@ -411,7 +643,12 @@ where Ok(blocks) } - fn broadcast_new_head(&self, block: ExecutedBlock) { + fn broadcast_new_head( + &self, + block: ExecutedBlock, + dag_parents: Option>, + next_tips: Option>, + ) { if let Some(metrics) = self.metrics.as_ref() { metrics .chain_select_head_total @@ -419,74 +656,341 @@ where .inc() } - if let Err(e) = self.bus.broadcast(NewHeadBlock(Arc::new(block))) { + if let Err(e) = self + .bus + .broadcast(NewHeadBlock(Arc::new(block), dag_parents, next_tips)) + { error!("Broadcast NewHeadBlock error: {:?}", e); } } - fn broadcast_new_branch(&self, block: ExecutedBlock) { + fn broadcast_new_branch( + &self, + block: ExecutedBlock, + dag_block_parents: Option>, + ) { if let Some(metrics) = self.metrics.as_ref() { metrics .chain_select_head_total .with_label_values(&["new_branch"]) .inc() } - if let Err(e) = self.bus.broadcast(NewBranch(Arc::new(block))) { + if let Err(e) = self + .bus + .broadcast(NewBranch(Arc::new(block), dag_block_parents)) + { error!("Broadcast NewBranch error: {:?}", e); } } - fn connect_inner(&mut self, block: Block) -> Result { - let block_id = block.id(); - if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH - && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT - { - debug!("barnard hard fork {}", block_id); - return Err(ConnectBlockError::BarnardHardFork(Box::new(block)).into()); - } - if self.main.current_header().id() == block_id { - debug!("Repeat connect, current header is {} already.", block_id); - return Ok(ConnectOk::Duplicate); - } - if self.main.current_header().id() == block.header().parent_hash() - && !self.block_exist(block_id)? - { - let executed_block = self.main.apply(block)?; - let enacted_blocks = vec![executed_block.block().clone()]; - self.do_new_head(executed_block, 1, enacted_blocks, 0, vec![])?; - return Ok(ConnectOk::ExeConnectMain); - } - let (block_info, fork) = self.find_or_fork(block.header())?; + fn switch_branch( + &mut self, + block: Block, + dag_block_parents: Option>, + dag_block_next_parent: Option, + next_tips: &mut Option>, + ) -> Result { + let (block_info, fork) = self.find_or_fork( + block.header(), + dag_block_next_parent, + dag_block_parents.clone(), + )?; match (block_info, fork) { //block has been processed in some branch, so just trigger a head selection. - (Some(_block_info), Some(branch)) => { + (Some(block_info), Some(branch)) => { + // both are different, select one debug!( "Block {} has been processed, trigger head selection, total_difficulty: {}", - block_id, + block_info.block_id(), branch.get_total_difficulty()? ); - self.select_head(branch)?; - Ok(ConnectOk::Duplicate) + let exe_block = branch.head_block(); + self.select_head(branch, dag_block_parents)?; + if let Some(new_tips) = next_tips { + new_tips.push(block_info.block_id().clone()); + } + Ok(ConnectOk::Duplicate(exe_block)) } //block has been processed, and its parent is main chain, so just connect it to main chain. (Some(block_info), None) => { - let executed_block = self.main.connect(ExecutedBlock { - block: block.clone(), - block_info, - })?; + // both are identical + let block_id: HashValue = block_info.block_id().clone(); + let executed_block = self.main.connect( + ExecutedBlock { + block: block.clone(), + block_info, + dag_parent: dag_block_next_parent, + }, + next_tips, + )?; info!( "Block {} main has been processed, trigger head selection", - block_id + block_id, ); - self.do_new_head(executed_block, 1, vec![block], 0, vec![])?; - Ok(ConnectOk::Connect) + self.do_new_head(executed_block.clone(), 1, vec![block], 0, vec![])?; + Ok(ConnectOk::Connect(executed_block)) } (None, Some(mut branch)) => { - let _executed_block = branch.apply(block)?; - self.select_head(branch)?; - Ok(ConnectOk::ExeConnectBranch) + // the block is not in the block, but the parent is + let result = branch.apply(block, dag_block_next_parent, next_tips); + let executed_block = result?; + self.select_head(branch, dag_block_parents)?; + Ok(ConnectOk::ExeConnectBranch(executed_block)) } (None, None) => Err(ConnectBlockError::FutureBlock(Box::new(block)).into()), } } + + fn connect_to_main( + &mut self, + block: Block, + dag_block_parents: Option>, + dag_block_next_parent: Option, + next_tips: &mut Option>, + ) -> Result { + let block_id = block.id(); + if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH + && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT + { + debug!("barnard hard fork {}", block_id); + return Err(ConnectBlockError::BarnardHardFork(Box::new(block)).into()); + } + if self.main.current_header().id() == block_id { + debug!("Repeat connect, current header is {} already.", block_id); + return Ok(ConnectOk::MainDuplicate); + } + if let Some(parents) = dag_block_parents { + assert!(parents.len() > 0); + // let color = self + // .dag + // .lock() + // .as_mut() + // .expect("failed to get the mut dag object") + // .commit_header(&Header::new(block.header().clone(), parents.clone()))?; + let color = ColoringOutput::Blue( + 0, + BlockHashMap::with_capacity(3), // k + ); + match color { + ColoringOutput::Blue(_, _) => { + if self + .main + .current_tips_hash() + .expect("in dag block, the tips hash must exist") + == block.header().parent_hash() + && !self.block_exist(block_id)? + { + return self.apply_and_select_head( + block, + Some(parents), + dag_block_next_parent, + next_tips, + ); + } + self.switch_branch(block, Some(parents), dag_block_next_parent, next_tips) + } + ColoringOutput::Red => { + self.switch_branch(block, Some(parents), dag_block_next_parent, next_tips) + } + } + } else { + if self.main.current_header().id() == block.header().parent_hash() + && !self.block_exist(block_id)? + { + return self.apply_and_select_head(block, None, None, &mut None); + } + // todo: should switch dag together + self.switch_branch(block, None, None, &mut None) + } + } + + fn apply_and_select_head( + &mut self, + block: Block, + dag_block_parents: Option>, + dag_block_next_parent: Option, + next_tips: &mut Option>, + ) -> Result { + let executed_block = self.main.apply(block, dag_block_next_parent, next_tips)?; + let enacted_blocks = vec![executed_block.block().clone()]; + self.do_new_head(executed_block.clone(), 1, enacted_blocks, 0, vec![])?; + return Ok(ConnectOk::ExeConnectMain(executed_block)); + } + + fn connect_inner( + &mut self, + block: Block, + tips_headers: Option>, + ) -> Result { + let block_id = block.id(); + if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH + && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT + { + debug!("barnard hard fork {}", block_id); + return Err(ConnectBlockError::BarnardHardFork(Box::new(block)).into()); + } + if self.main.current_header().id() == block_id { + debug!("Repeat connect, current header is {} already.", block_id); + return Ok(ConnectOk::MainDuplicate); + } + + // if it received a block with tips, it is a dag block + if let Some(dag_block_parents) = tips_headers { + // tips header, check the dag time window to see if it is should apply the blocks + // checkout if it is time to settle down + let time_service = DagBlockTimeWindowService::new( + 3 * 1000000, + self.config.net().time_service().clone(), + ); + let block_id = block.header.id(); + self.dag_block_pool + .lock() + .unwrap() + .push((block, dag_block_parents.clone())); + + let _testing = self + .dag + .lock() + .unwrap() + .push_parent_children(block_id, Arc::new(dag_block_parents.clone())); + + // if self.dag_block_pool.lock().unwrap().len() < 3 { + // // TimeWindowResult::InTimeWindow => { + // return Ok(ConnectOk::DagPending); + // } else { + // TimeWindowResult::BeforeTimeWindow => { + // return Err(ConnectBlockError::DagBlockBeforeTimeWindow(Box::new(block)).into()) + // } + // TimeWindowResult::AfterTimeWindow => { + // dump the block in the time window pool and put the block into the next time window pool + // self.main.status().tips_hash = None; // set the tips to None, and in connect_to_main, the block will be added to the tips + + // 2, get the new tips and clear the blocks in the pool + let dag_blocks = self.dag_block_pool.lock().unwrap().clone(); + self.dag_block_pool.lock().unwrap().clear(); + + return self.execute_dag_block_in_pool(dag_blocks, dag_block_parents); + // } + } else { + // normal block, just connect to main + let mut next_tips = Some(vec![]); + let executed_block = self + .connect_to_main(block, None, None, &mut next_tips)? + .clone(); + if let Some(block) = executed_block.block() { + self.broadcast_new_head(block.clone(), None, None); + } + return Ok(executed_block); + } + } + + #[cfg(test)] + pub fn execute_dag_block_pool(&mut self) -> Result { + let mut dag_blocks = self.dag_block_pool.lock().unwrap().clone(); + self.dag_block_pool.lock().unwrap().clear(); + return self.execute_dag_block_in_pool( + dag_blocks, + self.main + .status() + .tips_hash + .expect("dag block must has current tips") + .clone(), + ); + } + + pub fn execute_dag_block_in_pool( + &mut self, + mut dag_blocks: Vec<(Block, Vec)>, + current_tips: Vec, + ) -> Result { + // 3, process the blocks that are got from the pool + // sort by id + dag_blocks.sort_by_key(|(block, _)| block.header().id()); + + let mut dag_block_next_parent = current_tips + .iter() + .max() + .expect("tips must be larger than 0") + .clone(); + let mut next_tips = Some(vec![]); + let mut executed_blocks = vec![]; + // connect the block one by one + dag_blocks + .into_iter() + .try_fold((), |_, (block, dag_block_parents)| { + let next_transaction_parent = block.header().id(); + let result = self.connect_to_main( + block, + Some(dag_block_parents.clone()), + Some(dag_block_next_parent), + &mut next_tips, + ); + match result { + std::result::Result::Ok(connect_ok) => { + executed_blocks.push((connect_ok.block().clone(), dag_block_parents)); + dag_block_next_parent = next_transaction_parent; + Ok(()) + } + Err(error) => { + bail!("apply_and_select_head failed, error: {}", error.to_string()) + } + } + })?; + + match next_tips { + Some(new_tips) => { + if new_tips.is_empty() { + bail!("no new block has been executed successfully!"); + } + + let mut connected = self.main.is_head_of_dag_accumulator(new_tips.clone())?; + if self.main.dag_parents_in_tips(new_tips.clone())? { + // 1, write to disc + if !connected { + self.main.append_dag_accumulator_leaf(new_tips.clone())?; + connected = true; + } + } + + if connected { + // 2, broadcast the blocks sorted by their id + executed_blocks + .iter() + .for_each(|(exe_block, dag_block_parents)| { + if let Some(block) = exe_block { + self.broadcast_new_head( + block.clone(), + Some(dag_block_parents.clone()), + Some(new_tips.clone()), + ); + } + }); + } + + return executed_blocks + .last() + .map(|(exe_block, _)| { + if connected { + ConnectOk::ExeConnectMain( + exe_block + .as_ref() + .expect("exe block should not be None!") + .clone(), + ) + } else { + ConnectOk::ExeConnectBranch( + exe_block + .as_ref() + .expect("exe block should not be None!") + .clone(), + ) + } + }) + .ok_or_else(|| format_err!("no block has been executed successfully!")); + } + None => { + unreachable!("next tips should not be None"); + } + }; + } } diff --git a/sync/src/sync.rs b/sync/src/sync.rs index abfd0ddd6b..c58d22e46d 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -3,16 +3,19 @@ use crate::block_connector::BlockConnectorService; use crate::sync_metrics::SyncMetrics; -use crate::tasks::{full_sync_task, AncestorEvent, SyncFetcher}; +use crate::tasks::{full_sync_task, sync_dag_full_task, AncestorEvent, SyncFetcher}; use crate::verified_rpc_client::{RpcVerifyError, VerifiedRpcClient}; use anyhow::{format_err, Result}; +use futures::executor::block_on; use futures::FutureExt; use futures_timer::Delay; use network_api::peer_score::PeerScoreMetrics; use network_api::{PeerId, PeerProvider, PeerSelector, PeerStrategy, ReputationChange}; +use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_chain::BlockChain; -use starcoin_chain_api::ChainReader; +use starcoin_chain_api::{ChainReader, ChainWriter}; use starcoin_config::NodeConfig; +use starcoin_consensus::BlockDAG; use starcoin_executor::VMMetrics; use starcoin_logger::prelude::*; use starcoin_network::NetworkServiceRef; @@ -21,7 +24,7 @@ use starcoin_service_registry::{ ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, }; use starcoin_storage::block_info::BlockInfoStore; -use starcoin_storage::{BlockStore, Storage}; +use starcoin_storage::{BlockStore, Storage, Store, SyncFlexiDagStore}; use starcoin_sync_api::{ PeerScoreRequest, PeerScoreResponse, SyncCancelRequest, SyncProgressReport, SyncProgressRequest, SyncServiceHandler, SyncStartRequest, SyncStatusRequest, SyncTarget, @@ -31,7 +34,8 @@ use starcoin_types::block::BlockIdAndNumber; use starcoin_types::startup_info::ChainStatus; use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::{NewHeadBlock, SyncStatusChangeEvent, SystemStarted}; -use std::sync::Arc; +use std::result::Result::Ok; +use std::sync::{Arc, Mutex}; use std::time::Duration; use stream_task::{TaskError, TaskEventCounterHandle, TaskHandle}; @@ -89,8 +93,29 @@ impl SyncService { .metrics .registry() .and_then(|registry| PeerScoreMetrics::register(registry).ok()); + // let genesis = storage + // .get_genesis()? + // .ok_or_else(|| format_err!("Can not find genesis hash in storage."))?; + let dag_accumulator_info = + match storage.get_dag_accumulator_info(head_block_info.block_id().clone())? { + Some(info) => Some(info), + None => { + warn!( + "Can not find dag accumulator info by head block id: {}, use genesis info.", + head_block_info.block_id(), + ); + None + } + }; Ok(Self { - sync_status: SyncStatus::new(ChainStatus::new(head_block.header, head_block_info)), + sync_status: SyncStatus::new( + ChainStatus::new( + head_block.header.clone(), + head_block_info, + Some(storage.get_tips_by_block_id(head_block_hash)?), + ), + dag_accumulator_info, + ), stage: SyncStage::NotStart, config, storage, @@ -143,6 +168,7 @@ impl SyncService { } let network = ctx.get_shared::()?; + let block_chain_service = ctx.service_ref::()?.clone(); let storage = self.storage.clone(); let self_ref = ctx.self_ref(); let connector_service = ctx @@ -152,6 +178,19 @@ impl SyncService { let peer_score_metrics = self.peer_score_metrics.clone(); let sync_metrics = self.metrics.clone(); let vm_metrics = self.vm_metrics.clone(); + + let dag_accumulator_store = ctx + .get_shared::>() + .expect("storage must exist") + .get_accumulator_store(AccumulatorStoreType::SyncDag); + let dag_accumulator_snapshot = ctx + .get_shared::>() + .expect("storage must exist") + .get_accumulator_snapshot_storage(); + + let dag = ctx.get_shared::>>()?; + + let test_storage = storage.clone(); let fut = async move { let peer_select_strategy = peer_strategy.unwrap_or_else(|| config.sync.peer_select_strategy()); @@ -220,36 +259,70 @@ impl SyncService { peer_selector.clone(), network.clone(), )); - if let Some(target) = + + // for testing, we start dag sync directly + if let Some((target, op_dag_accumulator_info)) = rpc_client.get_best_target(current_block_info.get_total_difficulty())? { - info!("[sync] Find target({}), total_difficulty:{}, current head({})'s total_difficulty({})", target.target_id.id(), target.block_info.total_difficulty, current_block_id, current_block_info.total_difficulty); - - let (fut, task_handle, task_event_handle) = full_sync_task( - current_block_id, - target.clone(), - skip_pow_verify, - config.net().time_service(), - storage.clone(), - connector_service.clone(), - rpc_client.clone(), - self_ref.clone(), - network.clone(), - config.sync.max_retry_times(), - sync_metrics.clone(), - vm_metrics.clone(), - )?; - - self_ref.notify(SyncBeginEvent { - target, - task_handle, - task_event_handle, - peer_selector, - })?; - if let Some(sync_task_total) = sync_task_total.as_ref() { - sync_task_total.with_label_values(&["start"]).inc(); + if let Some(target_accumulator_info) = op_dag_accumulator_info { + let local_dag_accumulator_info = storage + .get_dag_accumulator_info(current_block_id)? + .expect("current dag accumulator info should exist"); + let (fut, task_handle, task_event_handle) = sync_dag_full_task( + local_dag_accumulator_info, + target_accumulator_info, + rpc_client.clone(), + dag_accumulator_store, + dag_accumulator_snapshot, + storage.clone(), + config.net().time_service(), + vm_metrics.clone(), + connector_service.clone(), + network.clone(), + skip_pow_verify, + dag.clone(), + block_chain_service.clone(), + )?; + self_ref.notify(SyncBeginEvent { + target, + task_handle, + task_event_handle, + peer_selector, + })?; + if let Some(sync_task_total) = sync_task_total.as_ref() { + sync_task_total.with_label_values(&["start"]).inc(); + } + Ok(Some(fut.await?)) + } else { + info!("[sync] Find target({}), total_difficulty:{}, current head({})'s total_difficulty({})", target.target_id.id(), target.block_info.total_difficulty, current_block_id, current_block_info.total_difficulty); + + let (fut, task_handle, task_event_handle) = full_sync_task( + current_block_id, + target.clone(), + skip_pow_verify, + config.net().time_service(), + storage.clone(), + connector_service.clone(), + rpc_client.clone(), + self_ref.clone(), + network.clone(), + config.sync.max_retry_times(), + block_chain_service.clone(), + sync_metrics.clone(), + vm_metrics.clone(), + )?; + + self_ref.notify(SyncBeginEvent { + target, + task_handle, + task_event_handle, + peer_selector, + })?; + if let Some(sync_task_total) = sync_task_total.as_ref() { + sync_task_total.with_label_values(&["start"]).inc(); + } + Ok(Some(fut.await?)) } - Ok(Some(fut.await?)) } else { debug!("[sync]No best peer to request, current is beast."); Ok(None) @@ -271,7 +344,18 @@ impl SyncService { |result: Result, anyhow::Error>| async move { let cancel = match result { Ok(Some(chain)) => { - info!("[sync] Sync to latest block: {:?}", chain.current_header()); + info!("[sync] Sync to latest block: {:?}", chain.status()); + info!("[sync] Sync to latest accumulator info: {:?}", chain.get_current_dag_accumulator_info()); + + let startup_info = test_storage + .get_startup_info().unwrap() + .ok_or_else(|| format_err!("Startup info should exist.")).unwrap(); + let current_block_id = startup_info.main; + + let local_dag_accumulator_info = test_storage + .get_dag_accumulator_info(current_block_id).unwrap() + .expect("current dag accumulator info should exist"); + if let Some(sync_task_total) = sync_task_total.as_ref() { sync_task_total.with_label_values(&["done"]).inc(); } @@ -523,6 +607,8 @@ impl CheckSyncEvent { impl EventHandler for SyncService { fn handle_event(&mut self, msg: CheckSyncEvent, ctx: &mut ServiceContext) { + // comment temporarily, for the dag branch, starcoin will sync dag only + // it will add some logic to determine which part to sync in the future if let Err(e) = self.check_and_start_sync(msg.peers, msg.skip_pow_verify, msg.strategy, ctx) { error!("[sync] Check sync error: {:?}", e); @@ -577,11 +663,17 @@ impl EventHandler for SyncService { impl EventHandler for SyncService { fn handle_event(&mut self, msg: NewHeadBlock, ctx: &mut ServiceContext) { - let NewHeadBlock(block) = msg; + let NewHeadBlock(block, _dag_parents, next_tips) = msg; if self.sync_status.update_chain_status(ChainStatus::new( block.header().clone(), block.block_info.clone(), + next_tips, )) { + self.sync_status.update_dag_accumulator_info( + self.storage + .get_dag_accumulator_info(block.header().id()) + .expect("dag accumulator info must exist"), + ); ctx.broadcast(SyncStatusChangeEvent(self.sync_status.clone())); } } diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 3fe42d66cc..022d2dabc1 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -1,9 +1,10 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::tasks::{BlockConnectedEvent, BlockConnectedEventHandle, BlockFetcher, BlockLocalStore}; +use crate::block_connector::BlockConnectorService; +use crate::tasks::{BlockConnectedEventHandle, BlockFetcher, BlockLocalStore}; use crate::verified_rpc_client::RpcVerifyError; -use anyhow::{format_err, Result}; +use anyhow::{format_err, Ok, Result}; use futures::future::BoxFuture; use futures::FutureExt; use network_api::PeerId; @@ -12,38 +13,78 @@ use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::{verifier::BasicVerifier, BlockChain}; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, ExecutedBlock}; use starcoin_config::G_CRATE_VERSION; +use starcoin_consensus::BlockDAG; +use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; use starcoin_storage::BARNARD_HARD_FORK_HASH; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; use stream_task::{CollectorState, TaskError, TaskResultCollector, TaskState}; -use super::{BlockConnectAction, BlockConnectedFinishEvent}; +use super::{BlockConnectAction, BlockConnectedEvent, BlockConnectedFinishEvent}; #[derive(Clone, Debug)] pub struct SyncBlockData { pub(crate) block: Block, pub(crate) info: Option, pub(crate) peer_id: Option, + pub(crate) accumulator_root: Option, // the block belongs to this accumulator leaf + pub(crate) count_in_leaf: u64, // the number of the block in the accumulator leaf + pub(crate) dag_block_headers: Option>, + pub(crate) dag_transaction_header: Option, } impl SyncBlockData { - pub fn new(block: Block, block_info: Option, peer_id: Option) -> Self { + pub fn new( + block: Block, + block_info: Option, + peer_id: Option, + accumulator_root: Option, + count_in_leaf: u64, + dag_block_headers: Option>, + dag_transaction_header: Option, + ) -> Self { Self { block, info: block_info, peer_id, + accumulator_root, + count_in_leaf, + dag_block_headers, + dag_transaction_header, } } } #[allow(clippy::from_over_into)] -impl Into<(Block, Option, Option)> for SyncBlockData { - fn into(self) -> (Block, Option, Option) { - (self.block, self.info, self.peer_id) +impl + Into<( + Block, + Option, + Option, + Option>, + Option, + )> for SyncBlockData +{ + fn into( + self, + ) -> ( + Block, + Option, + Option, + Option>, + Option, + ) { + ( + self.block, + self.info, + self.peer_id, + self.dag_block_headers, + self.dag_transaction_header, + ) } } @@ -128,8 +169,11 @@ impl TaskState for BlockSyncTask { .fetch_blocks(no_exist_block_ids) .await? .into_iter() - .fold(result_map, |mut result_map, (block, peer_id)| { - result_map.insert(block.id(), SyncBlockData::new(block, None, peer_id)); + .fold(result_map, |mut result_map, (block, peer_id, _, _)| { + result_map.insert( + block.id(), + SyncBlockData::new(block, None, peer_id, None, 1, None, None), + ); result_map }) }; @@ -149,7 +193,9 @@ impl TaskState for BlockSyncTask { .fetch_blocks(block_ids) .await? .into_iter() - .map(|(block, peer_id)| SyncBlockData::new(block, None, peer_id)) + .map(|(block, peer_id, _, _)| { + SyncBlockData::new(block, None, peer_id, None, 1, None, None) + }) .collect()) } } @@ -184,12 +230,16 @@ impl TaskState for BlockSyncTask { pub struct BlockCollector { //node's current block info current_block_info: BlockInfo, - target: SyncTarget, + target: Option, // the block chain init by ancestor chain: BlockChain, event_handle: H, peer_provider: N, skip_pow_verify: bool, + last_accumulator_root: HashValue, + dag_block_pool: Vec, + target_accumulator_root: HashValue, + dag: Option>>, } impl BlockCollector @@ -199,12 +249,19 @@ where { pub fn new_with_handle( current_block_info: BlockInfo, - target: SyncTarget, + target: Option, chain: BlockChain, event_handle: H, peer_provider: N, skip_pow_verify: bool, + target_accumulator_root: HashValue, + dag: Option>>, ) -> Self { + if let Some(dag) = &dag { + dag.lock() + .expect("failed to lock the dag") + .clear_missing_block(); + } Self { current_block_info, target, @@ -212,12 +269,21 @@ where event_handle, peer_provider, skip_pow_verify, + last_accumulator_root: HashValue::zero(), + dag_block_pool: Vec::new(), + target_accumulator_root, + dag: dag.clone(), } } #[cfg(test)] - pub fn apply_block_for_test(&mut self, block: Block) -> Result<()> { - self.apply_block(block, None) + pub fn apply_block_for_test( + &mut self, + block: Block, + dag_parent: Option, + next_tips: &mut Option>, + ) -> Result<()> { + self.apply_block(block, None, dag_parent, next_tips) } fn notify_connected_block( @@ -226,6 +292,7 @@ where block_info: BlockInfo, action: BlockConnectAction, state: CollectorState, + dag_parents: Option> ) -> Result { let total_difficulty = block_info.get_total_difficulty(); @@ -251,6 +318,7 @@ where // second, construct the block connect event. let block_connect_event = BlockConnectedEvent { block, + dag_parents, feedback: sender, action, }; @@ -270,7 +338,7 @@ where while count < 3 { count = count.saturating_add(1); match receiver.as_mut().unwrap().try_next() { - Ok(_) => { + std::result::Result::Ok(_) => { break; } Err(_) => { @@ -283,7 +351,13 @@ where Ok(state) } - fn apply_block(&mut self, block: Block, peer_id: Option) -> Result<()> { + fn apply_block( + &mut self, + block: Block, + peer_id: Option, + dag_parent: Option, + next_tips: &mut Option>, + ) -> Result<()> { if let Some((_failed_block, pre_peer_id, err, version)) = self .chain .get_storage() @@ -312,9 +386,9 @@ where } let apply_result = if self.skip_pow_verify { self.chain - .apply_with_verifier::(block.clone()) + .apply_with_verifier::(block.clone(), dag_parent, next_tips) } else { - self.chain.apply(block.clone()) + self.chain.apply(block.clone(), dag_parent, next_tips) }; if let Err(err) = apply_result { let error_msg = err.to_string(); @@ -323,7 +397,7 @@ where error_msg, peer_id ); match err.downcast::() { - Ok(connect_error) => match connect_error { + std::result::Result::Ok(connect_error) => match connect_error { ConnectBlockError::FutureBlock(block) => { Err(ConnectBlockError::FutureBlock(block).into()) } @@ -348,64 +422,166 @@ where Ok(()) } } -} -impl TaskResultCollector for BlockCollector -where - N: PeerProvider + 'static, - H: BlockConnectedEventHandle + 'static, -{ - type Output = BlockChain; + fn broadcast_dag_chain_block(&mut self, broadcast_blocks: Vec<(Block, BlockInfo, Option>, BlockConnectAction)>) -> Result { + let state = if self.last_accumulator_root == self.target_accumulator_root { + CollectorState::Enough + } else { + CollectorState::Need + }; - fn collect(&mut self, item: SyncBlockData) -> Result { - let (block, block_info, peer_id) = item.into(); + let last_index = broadcast_blocks.len() - 1; + broadcast_blocks.into_iter().enumerate().for_each(|(index, (block, block_info, dag_parents, action))| { + if last_index == index && state == CollectorState::Enough { + let _ = self.notify_connected_block(block, block_info, action, CollectorState::Enough, dag_parents); + } else { + let _ = self.notify_connected_block(block, block_info, action, CollectorState::Need, dag_parents); + } + }); + + return Ok(state); + } + + fn broadcast_single_chain_block(&mut self, block: Block, block_info: BlockInfo, action: BlockConnectAction) -> Result { + let target = self + .target + .as_ref() + .expect("the process is for single chain"); + let state = if block_info.block_accumulator_info.num_leaves + == target.block_info.block_accumulator_info.num_leaves + { + if block_info != target.block_info { + Err(TaskError::BreakError( + RpcVerifyError::new_with_peers( + target.peers.clone(), + format!( + "Verify target error, expect target: {:?}, collect target block_info:{:?}", + target.block_info, + block_info + ), + ) + .into(), + ) + .into()) + } else { + Ok(CollectorState::Enough) + } + } else { + Ok(CollectorState::Need) + }; + + self.notify_connected_block(block, block_info, action, state?, None) + } + + fn collect_item( + &mut self, + item: SyncBlockData, + next_tips: &mut Option>, + ) -> Result<(Block, BlockInfo, Option>, BlockConnectAction)> { + let (block, block_info, peer_id, dag_parents, dag_transaction_header) = item.into(); + let block_id = block.id(); let timestamp = block.header().timestamp(); - let (block_info, action) = match block_info { + + if let Some(parents) = dag_parents.clone() { + if let Some(dag) = &self.dag { + // let color = dag + // .lock() + // .unwrap() + // .commit_header(&Header::new(block.header().clone(), parents.clone()))?; + // if let ColoringOutput::Red = color { + // panic!("the red block should not be applied or connected!"); + // } + let _ = dag.lock().unwrap().push_parent_children(block_id, Arc::new(parents)); + } else { + panic!("in dag sync, the dag should not be None") + } + } + + return match block_info { Some(block_info) => { //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. //So, we just need to update chain and continue - self.chain.connect(ExecutedBlock { - block: block.clone(), - block_info: block_info.clone(), - })?; - (block_info, BlockConnectAction::ConnectExecutedBlock) + self.chain.connect( + ExecutedBlock { + block: block.clone(), + block_info: block_info.clone(), + dag_parent: dag_transaction_header, + }, + next_tips, + )?; + let block_info = self.chain.status().info; + Ok((block, block_info, dag_parents, BlockConnectAction::ConnectExecutedBlock)) } None => { - self.apply_block(block.clone(), peer_id)?; + self.apply_block(block.clone(), peer_id, dag_transaction_header, next_tips)?; self.chain.time_service().adjust(timestamp); - ( - self.chain.status().info, - BlockConnectAction::ConnectNewBlock, - ) + let block_info = self.chain.status().info; + Ok((block, block_info, dag_parents, BlockConnectAction::ConnectNewBlock)) } }; + } +} - //verify target - let state: Result = - if block_info.block_accumulator_info.num_leaves - == self.target.block_info.block_accumulator_info.num_leaves - { - if block_info != self.target.block_info { - Err(TaskError::BreakError( - RpcVerifyError::new_with_peers( - self.target.peers.clone(), - format!( - "Verify target error, expect target: {:?}, collect target block_info:{:?}", - self.target.block_info, - block_info - ), - ) - .into(), - ) - .into()) - } else { - Ok(CollectorState::Enough) - } +impl TaskResultCollector for BlockCollector +where + N: PeerProvider + 'static, + H: BlockConnectedEventHandle + 'static, +{ + type Output = BlockChain; + + fn collect(&mut self, item: SyncBlockData) -> Result { + let mut process_block_pool = vec![]; + if item.accumulator_root.is_some() { + // it is a flexidag + self.dag_block_pool.push(item.clone()); + self.last_accumulator_root = item.accumulator_root.unwrap(); + + if item.count_in_leaf != self.dag_block_pool.len() as u64 { + return Ok(CollectorState::Need); } else { - Ok(CollectorState::Need) - }; + process_block_pool = std::mem::take(&mut self.dag_block_pool); + + self.chain.status().tips_hash = Some( + process_block_pool + .iter() + .clone() + .map(|item| item.block.header().id()) + .collect(), + ); + } + } else { + // it is a single chain + process_block_pool.push(item); + } + + assert!(!process_block_pool.is_empty()); - self.notify_connected_block(block, block_info, action, state?) + let mut next_tips = Some(vec![]); + let mut block_to_broadcast = vec![]; + process_block_pool.into_iter().try_fold((), |_, item| { + block_to_broadcast.push(self.collect_item(item, &mut next_tips)) + }); + + //verify target + match self.target { + Some(_) => { + assert_eq!(block_to_broadcast.len(), 1, "in single chain , block_info should exist!"); + let (block, block_info, _, action) = block_to_broadcast.pop().unwrap(); + // self.check_if_sync_complete(block_info) + self.broadcast_single_chain_block(block, block_info, action) + } + None => { + // dag + assert!(!next_tips + .as_ref() + .expect("next_tips should not be None") + .is_empty()); + self.chain.append_dag_accumulator_leaf( + next_tips.expect("next_tips should not be None"), + )?; + self.broadcast_dag_chain_block(block_to_broadcast) + } + } } fn finish(self) -> Result { diff --git a/sync/src/tasks/inner_sync_task.rs b/sync/src/tasks/inner_sync_task.rs index 8d0f70e953..43b53c16e2 100644 --- a/sync/src/tasks/inner_sync_task.rs +++ b/sync/src/tasks/inner_sync_task.rs @@ -1,12 +1,17 @@ -use crate::tasks::{ - AccumulatorCollector, BlockAccumulatorSyncTask, BlockCollector, BlockConnectedEventHandle, - BlockFetcher, BlockIdFetcher, BlockSyncTask, PeerOperator, +use crate::{ + block_connector::BlockConnectorService, + tasks::{ + AccumulatorCollector, BlockAccumulatorSyncTask, BlockCollector, BlockConnectedEventHandle, + BlockFetcher, BlockIdFetcher, BlockSyncTask, PeerOperator, + }, }; use anyhow::format_err; use network_api::PeerProvider; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_chain::BlockChain; +use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; +use starcoin_service_registry::ServiceRef; use starcoin_storage::Store; use starcoin_sync_api::SyncTarget; use starcoin_time_service::TimeService; @@ -81,6 +86,7 @@ where max_retry_times: u64, delay_milliseconds_on_error: u64, skip_pow_verify_when_sync: bool, + block_chain_service: ServiceRef, vm_metrics: Option, ) -> Result<(BlockChain, TaskHandle), TaskError> { let buffer_size = self.target.peers.len(); @@ -117,7 +123,7 @@ where ) .and_then(move |(ancestor, accumulator), event_handle| { let check_local_store = - ancestor_block_info.total_difficulty <= current_block_info.total_difficulty; + ancestor_block_info.total_difficulty < current_block_info.total_difficulty; let block_sync_task = BlockSyncTask::new( accumulator, @@ -135,11 +141,13 @@ where )?; let block_collector = BlockCollector::new_with_handle( current_block_info.clone(), - self.target.clone(), + Some(self.target.clone()), chain, self.block_event_handle.clone(), self.peer_provider.clone(), skip_pow_verify_when_sync, + HashValue::zero(), + None, ); Ok(TaskGenerator::new( block_sync_task, diff --git a/sync/src/tasks/mock.rs b/sync/src/tasks/mock.rs index 6b9ddb3296..2a52e3679f 100644 --- a/sync/src/tasks/mock.rs +++ b/sync/src/tasks/mock.rs @@ -338,12 +338,29 @@ impl BlockFetcher for SyncNodeMocker { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture<'_, Result)>>> { - let result: Result)>> = block_ids + ) -> BoxFuture< + '_, + Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + >, + > { + let result: Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + > = block_ids .into_iter() .map(|block_id| { if let Some(block) = self.chain().get_block(block_id)? { - Ok((block, None)) + Ok((block, None, None, None)) } else { Err(format_err!("Can not find block by id: {}", block_id)) } diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 7577beaa00..90eb1422cf 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -11,6 +11,7 @@ use futures::future::BoxFuture; use futures::{FutureExt, TryFutureExt}; use network_api::{PeerId, PeerProvider, PeerSelector}; use network_p2p_core::{NetRpcError, RpcErrorCode}; +use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_accumulator::MerkleAccumulator; use starcoin_chain::{BlockChain, ChainReader}; @@ -26,6 +27,7 @@ use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; use starcoin_types::startup_info::ChainStatus; use starcoin_types::U256; +use std::result::Result::Ok; use std::str::FromStr; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -36,43 +38,55 @@ use stream_task::{ }; pub trait SyncFetcher: PeerOperator + BlockIdFetcher + BlockFetcher + BlockInfoFetcher { - fn get_best_target(&self, min_difficulty: U256) -> Result> { + fn get_best_target( + &self, + min_difficulty: U256, + ) -> Result)>> { if let Some(best_peers) = self.peer_selector().bests(min_difficulty) { //TODO fast verify best peers by accumulator - let mut chain_statuses: Vec<(ChainStatus, Vec)> = + let mut chain_statuses: Vec<(ChainStatus, Vec, Option)> = best_peers .into_iter() .fold(vec![], |mut chain_statuses, peer| { let update = chain_statuses .iter_mut() - .find(|(chain_status, _peers)| { + .find(|(chain_status, _peers, _)| { peer.chain_info().status() == chain_status }) - .map(|(_chain_status, peers)| { + .map(|(_chain_status, peers, _)| { peers.push(peer.peer_id()); true }) .unwrap_or(false); if !update { - chain_statuses - .push((peer.chain_info().status().clone(), vec![peer.peer_id()])) + chain_statuses.push(( + peer.chain_info().status().clone(), + vec![peer.peer_id()], + peer.chain_info().dag_accumulator_info().clone(), + )) } chain_statuses }); //if all best peers block info is same, block_infos len should been 1, other use majority peers block_info if chain_statuses.len() > 1 { - chain_statuses.sort_by(|(_chain_status_1, peers_1), (_chain_status_2, peers_2)| { - peers_1.len().cmp(&peers_2.len()) - }); + chain_statuses.sort_by( + |(_chain_status_1, peers_1, _), (_chain_status_2, peers_2, _)| { + peers_1.len().cmp(&peers_2.len()) + }, + ); } - let (chain_status, peers) = chain_statuses.pop().expect("chain statuses should exist"); + let (chain_status, peers, dag_accumulator_info) = + chain_statuses.pop().expect("chain statuses should exist"); let header = chain_status.head; - Ok(Some(SyncTarget { - target_id: BlockIdAndNumber::new(header.id(), header.number()), - block_info: chain_status.info, - peers, - })) + Ok(Some(( + SyncTarget { + target_id: BlockIdAndNumber::new(header.id(), header.number()), + block_info: chain_status.info, + peers, + }, + dag_accumulator_info, + ))) } else { debug!( "get_best_target return None, total_peers_in_selector: {}, min_difficulty: {}", @@ -283,7 +297,16 @@ pub trait BlockFetcher: Send + Sync { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture)>>>; + ) -> BoxFuture< + Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + >, + >; } impl BlockFetcher for Arc @@ -293,7 +316,17 @@ where fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture<'_, Result)>>> { + ) -> BoxFuture< + '_, + Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + >, + > { BlockFetcher::fetch_blocks(self.as_ref(), block_ids) } } @@ -302,10 +335,27 @@ impl BlockFetcher for VerifiedRpcClient { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture<'_, Result)>>> { + ) -> BoxFuture< + '_, + Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + >, + > { self.get_blocks(block_ids.clone()) .and_then(|blocks| async move { - let results: Result)>> = block_ids + let results: Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + > = block_ids .iter() .zip(blocks) .map(|(id, block)| { @@ -376,7 +426,10 @@ impl BlockLocalStore for Arc { Some(block) => { let id = block.id(); let block_info = self.get_block_info(id)?; - Ok(Some(SyncBlockData::new(block, block_info, None))) + + Ok(Some(SyncBlockData::new( + block, block_info, None, None, 1, None, None, + ))) } None => Ok(None), }) @@ -393,6 +446,7 @@ pub enum BlockConnectAction { #[derive(Clone, Debug)] pub struct BlockConnectedEvent { pub block: Block, + pub dag_parents: Option>, pub feedback: Option>, pub action: BlockConnectAction, } @@ -546,6 +600,11 @@ mod find_ancestor_task; mod inner_sync_task; #[cfg(test)] pub(crate) mod mock; +mod sync_dag_accumulator_task; +mod sync_dag_block_task; +mod sync_dag_full_task; +mod sync_dag_protocol_trait; +mod sync_find_ancestor_task; #[cfg(test)] mod tests; @@ -554,6 +613,7 @@ pub use accumulator_sync_task::{AccumulatorCollector, BlockAccumulatorSyncTask}; pub use block_sync_task::{BlockCollector, BlockSyncTask}; pub use find_ancestor_task::{AncestorCollector, FindAncestorTask}; use starcoin_executor::VMMetrics; +pub use sync_dag_full_task::sync_dag_full_task; pub fn full_sync_task( current_block_id: HashValue, @@ -566,6 +626,7 @@ pub fn full_sync_task( ancestor_event_handle: A, peer_provider: N, max_retry_times: u64, + block_chain_service: ServiceRef, sync_metrics: Option, vm_metrics: Option, ) -> Result<( @@ -681,6 +742,7 @@ where max_retry_times, delay_milliseconds_on_error, skip_pow_verify, + block_chain_service.clone(), vm_metrics.clone(), ) .await?; diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index b9e731eab2..64839b4924 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -10,6 +10,7 @@ use crate::tasks::{ BlockCollector, BlockFetcher, BlockLocalStore, BlockSyncTask, FindAncestorTask, SyncFetcher, }; use crate::verified_rpc_client::RpcVerifyError; +use anyhow::Context; use anyhow::{format_err, Result}; use anyhow::{Context, Ok}; use futures::channel::mpsc::unbounded; @@ -199,17 +200,22 @@ pub async fn test_failed_block() -> Result<()> { }; let mut block_collector = BlockCollector::new_with_handle( chain_info.status().info.clone(), - target, + Some(target), chain, sender, DummyNetworkService::default(), true, + HashValue::zero(), + None, ); let header = BlockHeaderBuilder::random().with_number(1).build(); let body = BlockBody::new(Vec::new(), None); let failed_block = Block::new(header, body); let failed_block_id = failed_block.id(); - if block_collector.apply_block_for_test(failed_block).is_err() { + if block_collector + .apply_block_for_test(failed_block, None, &mut None) + .is_err() + { assert!(storage.get_failed_block_by_id(failed_block_id)?.is_some()); Ok(()) } else { @@ -688,13 +694,29 @@ impl BlockFetcher for MockBlockFetcher { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture)>>> { + ) -> BoxFuture< + Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + >, + > { let blocks = self.blocks.lock().unwrap(); - let result: Result)>> = block_ids + let result: Result< + Vec<( + Block, + Option, + Option>, + Option, + )>, + > = block_ids .iter() .map(|block_id| { if let Some(block) = blocks.get(block_id).cloned() { - Ok((block, None)) + Ok((block, None, None, None)) } else { Err(format_err!("Can not find block by id: {:?}", block_id)) } @@ -743,7 +765,7 @@ impl MockLocalBlockStore { ); self.store.lock().unwrap().insert( block.id(), - SyncBlockData::new(block.clone(), Some(block_info), None), + SyncBlockData::new(block.clone(), Some(block_info), None, None, 1, None, None), ); } } @@ -985,7 +1007,7 @@ async fn test_sync_target() { .unwrap() .unwrap(); let target = node2 - .get_better_target(genesis_chain_info.total_difficulty(), full_target, 10, 0) + .get_better_target(genesis_chain_info.total_difficulty(), full_target.0, 10, 0) .await .unwrap(); assert_eq!(target.peers.len(), 2); diff --git a/sync/src/verified_rpc_client.rs b/sync/src/verified_rpc_client.rs index fc4bc6f8f5..57c682d320 100644 --- a/sync/src/verified_rpc_client.rs +++ b/sync/src/verified_rpc_client.rs @@ -10,6 +10,7 @@ use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_accumulator::AccumulatorNode; use starcoin_crypto::hash::HashValue; use starcoin_logger::prelude::*; +use starcoin_network_rpc_api::dag_protocol; use starcoin_network_rpc_api::{ gen_client::NetworkRpcClient, BlockBody, GetAccumulatorNodeByNodeHash, GetBlockHeadersByNumber, GetBlockIds, GetTxnsWithHash, RawRpcClient, @@ -380,11 +381,19 @@ impl VerifiedRpcClient { pub async fn get_blocks( &self, ids: Vec, - ) -> Result)>>> { + ) -> Result< + Vec< + Option<( + Block, + Option, + Option>, + Option, + )>, + >, + > { let peer_id = self.select_a_peer()?; let start_time = Instant::now(); - let blocks: Vec> = - self.client.get_blocks(peer_id.clone(), ids.clone()).await?; + let blocks = self.client.get_blocks(peer_id.clone(), ids.clone()).await?; let time = (Instant::now() .saturating_duration_since(start_time) .as_millis()) as u32; @@ -395,7 +404,7 @@ impl VerifiedRpcClient { .zip(blocks) .map(|(id, block)| { if let Some(block) = block { - let actual_id = block.id(); + let actual_id = block.0.id(); if actual_id != id { warn!( "Get block by id: {:?} from peer: {:?}, but got block: {:?}", @@ -403,7 +412,7 @@ impl VerifiedRpcClient { ); None } else { - Some((block, Some(peer_id.clone()))) + Some((block.0, Some(peer_id.clone()), block.1, block.2)) } } else { None @@ -411,4 +420,36 @@ impl VerifiedRpcClient { }) .collect()) } + + pub async fn get_dag_accumulator_leaves( + &self, + req: dag_protocol::GetDagAccumulatorLeaves, + ) -> Result> { + let peer_id = self.select_a_peer()?; + self.client.get_dag_accumulator_leaves(peer_id, req).await + } + + pub async fn get_accumulator_leaf_detail( + &self, + req: dag_protocol::GetTargetDagAccumulatorLeafDetail, + ) -> Result>> { + let peer_id = self.select_a_peer()?; + match self.client.get_accumulator_leaf_detail(peer_id, req).await { + Ok(result) => Ok(result), + Err(error) => { + warn!( + "get_accumulator_leaf_detail return None, error: {}", + error.to_string() + ); + Ok(None) + } + } + } + + pub async fn get_dag_block_info( + &self, + _req: dag_protocol::GetSyncDagBlockInfo, + ) -> Result>> { + todo!() + } } diff --git a/test-helper/src/chain.rs b/test-helper/src/chain.rs index ba337c327b..bc69640391 100644 --- a/test-helper/src/chain.rs +++ b/test-helper/src/chain.rs @@ -27,7 +27,7 @@ pub fn gen_blockchain_with_blocks_for_test(count: u64, net: &ChainNetwork) -> Re let block = block_chain .consensus() .create_block(block_template, net.time_service().as_ref())?; - block_chain.apply(block)?; + block_chain.apply(block, None, &mut None)?; } Ok(block_chain) diff --git a/test-helper/src/network.rs b/test-helper/src/network.rs index 2e5faea961..42320fb360 100644 --- a/test-helper/src/network.rs +++ b/test-helper/src/network.rs @@ -16,7 +16,7 @@ use starcoin_service_registry::{ RegistryAsyncService, RegistryService, ServiceContext, ServiceFactory, ServiceRef, }; use starcoin_storage::block_info::BlockInfoStore; -use starcoin_storage::{BlockStore, Storage}; +use starcoin_storage::{BlockStore, Storage, SyncFlexiDagStore}; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; use std::any::Any; use std::borrow::Cow; @@ -184,7 +184,6 @@ impl ServiceFactory for MockNetworkServiceFactory { let peer_message_handle = MockPeerMessageHandler::default(); let config = ctx.get_shared::>()?; let storage = ctx.get_shared::>()?; - let genesis_hash = genesis.block().header().id(); let startup_info = storage.get_startup_info()?.unwrap(); let head_block_hash = startup_info.main; @@ -194,15 +193,22 @@ impl ServiceFactory for MockNetworkServiceFactory { let head_block_info = storage .get_block_info(head_block_hash)? .ok_or_else(|| format_err!("can't get block info by hash {}", head_block_hash))?; - let chain_status = ChainStatus::new(head_block_header, head_block_info); - let chain_info = - ChainInfo::new(config.net().chain_id(), genesis_hash, chain_status.clone()); + let dag_tips = storage.get_tips_by_block_id(head_block_hash)?; + let chain_status = + ChainStatus::new(head_block_header.clone(), head_block_info, Some(dag_tips)); + let dag_accumulator_info = storage.get_dag_accumulator_info(head_block_hash)?; + let chain_state_info = ChainInfo::new( + config.net().chain_id(), + genesis_hash, + chain_status.clone(), + dag_accumulator_info.clone(), + ); let actor_service = - NetworkActorService::new(config, chain_info, rpc, peer_message_handle.clone())?; + NetworkActorService::new(config, chain_state_info, rpc, peer_message_handle.clone())?; let network_service = actor_service.network_service(); let network_async_service = NetworkServiceRef::new(network_service, ctx.self_ref()); // set self sync status to synced for test. - let mut sync_status = SyncStatus::new(chain_status); + let mut sync_status = SyncStatus::new(chain_status, dag_accumulator_info); sync_status.sync_done(); ctx.notify(SyncStatusChangeEvent(sync_status)); diff --git a/test-helper/src/txn.rs b/test-helper/src/txn.rs index 10a419487a..cd0668a0fc 100644 --- a/test-helper/src/txn.rs +++ b/test-helper/src/txn.rs @@ -111,7 +111,7 @@ pub fn create_account_txn_sent_as_association( seq_num: u64, initial_amount: u128, expiration_timstamp_secs: u64, - net: &ChainNetwork, + net: &starcoin_config::ChainNetwork, ) -> SignedUserTransaction { let args = vec![ bcs_ext::to_bytes(new_account.address()).unwrap(), diff --git a/txpool/src/test.rs b/txpool/src/test.rs index e205b388e6..a25ae23841 100644 --- a/txpool/src/test.rs +++ b/txpool/src/test.rs @@ -227,6 +227,7 @@ async fn test_rollback() -> Result<()> { U256::from(1024u64), config.net().genesis_config().consensus(), None, + None, )?; let excluded_txns = open_block.push_txns(vec![txn])?; assert_eq!(excluded_txns.discarded_txns.len(), 0); @@ -257,7 +258,9 @@ async fn test_rollback() -> Result<()> { .unwrap(); txns.insert( 0, - Transaction::BlockMetadata(enacted_block.to_metadata(parent_block_header.gas_used())), + Transaction::BlockMetadata( + enacted_block.to_metadata(parent_block_header.gas_used(), None), + ), ); let root = starcoin_executor::block_execute(&chain_state, txns, u64::MAX, None)?.state_root; diff --git a/types/src/block.rs b/types/src/block.rs index 45704fa069..c681bb3333 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -7,7 +7,7 @@ use crate::genesis_config::{ChainId, ConsensusStrategy}; use crate::language_storage::CORE_CODE_ADDRESS; use crate::transaction::SignedUserTransaction; use crate::U256; -use bcs_ext::Sample; +use bcs_ext::{BCSCodec, Sample}; use schemars::{self, JsonSchema}; use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -20,6 +20,7 @@ use starcoin_crypto::{ use starcoin_vm_types::account_config::genesis_address; use starcoin_vm_types::transaction::authenticator::AuthenticationKey; use std::fmt::Formatter; +use std::hash::Hash; /// Type for block number. pub type BlockNumber = u64; @@ -717,7 +718,11 @@ impl Block { } } - pub fn to_metadata(&self, parent_gas_used: u64) -> BlockMetadata { + pub fn to_metadata( + &self, + parent_gas_used: u64, + dag_block_parent: Option, + ) -> BlockMetadata { let uncles = self .body .uncles @@ -725,8 +730,14 @@ impl Block { .map(|uncles| uncles.len() as u64) .unwrap_or(0); + let parent = if dag_block_parent.is_some() { + dag_block_parent.unwrap() + } else { + self.header.parent_hash() + }; + BlockMetadata::new( - self.header.parent_hash(), + parent, self.header.timestamp, self.header.author, self.header.author_auth_key, @@ -863,6 +874,8 @@ pub struct BlockTemplate { pub difficulty: U256, /// Block consensus strategy pub strategy: ConsensusStrategy, + /// Tips + pub tips_header: Option>, } impl BlockTemplate { @@ -876,6 +889,7 @@ impl BlockTemplate { difficulty: U256, strategy: ConsensusStrategy, block_metadata: BlockMetadata, + current_tips: Option>, ) -> Self { let (parent_hash, timestamp, author, _author_auth_key, _, number, _, _) = block_metadata.into_inner(); @@ -893,10 +907,33 @@ impl BlockTemplate { chain_id, difficulty, strategy, + tips_header: current_tips, } } pub fn into_block(self, nonce: u32, extra: BlockHeaderExtra) -> Block { + let header = BlockHeader::new( + self.generate_parent_header(), + self.timestamp, + self.number, + self.author, + self.txn_accumulator_root, + self.block_accumulator_root, + self.state_root, + self.gas_used, + self.difficulty, + self.body_hash, + self.chain_id, + nonce, + extra, + ); + Block { + header, + body: self.body, + } + } + + pub fn into_single_chain_block(self, nonce: u32, extra: BlockHeaderExtra) -> Block { let header = BlockHeader::new( self.parent_hash, self.timestamp, @@ -918,7 +955,16 @@ impl BlockTemplate { } } - pub fn as_raw_block_header(&self) -> RawBlockHeader { + fn generate_parent_header(&self) -> HashValue { + if self.tips_header.is_none() { + return self.parent_hash; + } + let mut tips = self.tips_header.as_ref().unwrap().clone(); + tips.sort(); + HashValue::sha3_256_of(&tips.encode().expect("dag parent must encode successfully")) + } + + pub fn as_raw_block_header_single_chain(&self) -> RawBlockHeader { RawBlockHeader { parent_hash: self.parent_hash, timestamp: self.timestamp, @@ -935,6 +981,37 @@ impl BlockTemplate { } } + pub fn as_raw_block_header(&self) -> RawBlockHeader { + RawBlockHeader { + parent_hash: self.generate_parent_header(), + timestamp: self.timestamp, + number: self.number, + author: self.author, + author_auth_key: None, + accumulator_root: self.txn_accumulator_root, + parent_block_accumulator_root: self.block_accumulator_root, + state_root: self.state_root, + gas_used: self.gas_used, + body_hash: self.body_hash, + difficulty: self.difficulty, + chain_id: self.chain_id, + } + } + + pub fn as_pow_header_blob_single_chain(&self) -> Vec { + let mut blob = Vec::new(); + let raw_header = self.as_raw_block_header_single_chain(); + let raw_header_hash = raw_header.crypto_hash(); + let mut dh = [0u8; 32]; + raw_header.difficulty.to_big_endian(&mut dh); + let extend_and_nonce = [0u8; 12]; + blob.extend_from_slice(raw_header_hash.to_vec().as_slice()); + blob.extend_from_slice(&extend_and_nonce); + blob.extend_from_slice(&dh); + + blob + } + pub fn as_pow_header_blob(&self) -> Vec { let mut blob = Vec::new(); let raw_header = self.as_raw_block_header(); @@ -942,10 +1019,10 @@ impl BlockTemplate { let mut dh = [0u8; 32]; raw_header.difficulty.to_big_endian(&mut dh); let extend_and_nonce = [0u8; 12]; - blob.extend_from_slice(raw_header_hash.to_vec().as_slice()); blob.extend_from_slice(&extend_and_nonce); blob.extend_from_slice(&dh); + blob } @@ -972,11 +1049,16 @@ impl BlockTemplate { pub struct ExecutedBlock { pub block: Block, pub block_info: BlockInfo, + pub dag_parent: Option, } impl ExecutedBlock { - pub fn new(block: Block, block_info: BlockInfo) -> Self { - ExecutedBlock { block, block_info } + pub fn new(block: Block, block_info: BlockInfo, dag_parent: Option) -> Self { + ExecutedBlock { + block, + block_info, + dag_parent, + } } pub fn total_difficulty(&self) -> U256 { diff --git a/types/src/lib.rs b/types/src/lib.rs index ec49aa8bed..7e4e65b6d8 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -104,3 +104,6 @@ pub mod sync_status; pub mod proof { pub use forkable_jellyfish_merkle::proof::SparseMerkleProof; } + +pub mod blockhash; +pub mod header; diff --git a/types/src/startup_info.rs b/types/src/startup_info.rs index d536020128..8ff3d94dc8 100644 --- a/types/src/startup_info.rs +++ b/types/src/startup_info.rs @@ -7,26 +7,36 @@ use bcs_ext::{BCSCodec, Sample}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use starcoin_accumulator::accumulator_info::AccumulatorInfo; +use starcoin_accumulator::MerkleAccumulator; use starcoin_crypto::HashValue; use starcoin_uint::U256; use starcoin_vm_types::genesis_config::ChainId; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::Formatter; +use std::hash::Hash; + /// The info of a chain. #[derive(Eq, PartialEq, Hash, Deserialize, Serialize, Clone, Debug)] pub struct ChainInfo { chain_id: ChainId, genesis_hash: HashValue, status: ChainStatus, + flexi_dag_accumulator_info: Option, } impl ChainInfo { - pub fn new(chain_id: ChainId, genesis_hash: HashValue, status: ChainStatus) -> Self { + pub fn new( + chain_id: ChainId, + genesis_hash: HashValue, + status: ChainStatus, + flexi_dag_accumulator_info: Option, + ) -> Self { Self { chain_id, genesis_hash, status, + flexi_dag_accumulator_info, } } @@ -43,15 +53,26 @@ impl ChainInfo { } pub fn update_status(&mut self, status: ChainStatus) { - self.status = status + self.status = status; + } + + pub fn update_dag_accumulator_info( + &mut self, + flexi_dag_accumulator_info: Option, + ) { + self.flexi_dag_accumulator_info = flexi_dag_accumulator_info; } pub fn head(&self) -> &BlockHeader { - self.status.head() + &self.status.head + } + + pub fn dag_accumulator_info(&self) -> &Option { + &self.flexi_dag_accumulator_info } pub fn total_difficulty(&self) -> U256 { - self.status.total_difficulty() + self.status.info.get_total_difficulty() } pub fn into_inner(self) -> (ChainId, HashValue, ChainStatus) { @@ -63,6 +84,12 @@ impl ChainInfo { chain_id: ChainId::new(rand::random()), genesis_hash: HashValue::random(), status: ChainStatus::random(), + flexi_dag_accumulator_info: Some(AccumulatorInfo::new( + HashValue::random(), + vec![], + rand::random::(), + rand::random::(), + )), } } } @@ -73,6 +100,7 @@ impl std::default::Default for ChainInfo { chain_id: ChainId::test(), genesis_hash: HashValue::default(), status: ChainStatus::sample(), + flexi_dag_accumulator_info: Some(AccumulatorInfo::default()), } } } @@ -94,11 +122,17 @@ pub struct ChainStatus { pub head: BlockHeader, /// Chain block info pub info: BlockInfo, + /// tips of the dag chain in dag accumulator snapshots + pub tips_hash: Option>, } impl ChainStatus { - pub fn new(head: BlockHeader, info: BlockInfo) -> Self { - Self { head, info } + pub fn new(head: BlockHeader, info: BlockInfo, tips_hash: Option>) -> Self { + Self { + head, + info, + tips_hash, + } } pub fn random() -> Self { @@ -120,8 +154,9 @@ impl ChainStatus { ), ); Self { - head, + head: head.clone(), info: block_info, + tips_hash: Some(vec![head.id()]), } } @@ -140,6 +175,14 @@ impl ChainStatus { pub fn into_inner(self) -> (BlockHeader, BlockInfo) { (self.head, self.info) } + + pub fn get_last_tip_block_id(&self) -> Option { + if let Some(tips) = &self.tips_hash { + tips.into_iter().max().cloned() + } else { + None + } + } } impl Sample for ChainStatus { @@ -147,6 +190,38 @@ impl Sample for ChainStatus { Self { head: BlockHeader::sample(), info: BlockInfo::sample(), + tips_hash: Some(vec![HashValue::zero()]), + } + } +} + +#[derive(Eq, PartialEq, Hash, Deserialize, Serialize, Clone, Debug)] +pub struct DagChainStatus { + pub flexi_dag_accumulator_info: AccumulatorInfo, +} + +impl DagChainStatus { + pub fn new(flexi_dag_accumulator_info: AccumulatorInfo) -> Self { + Self { + flexi_dag_accumulator_info, + } + } + + pub fn random() -> Self { + let head = BlockHeader::random(); + Self { + flexi_dag_accumulator_info: AccumulatorInfo::new( + head.block_accumulator_root(), + vec![], + rand::random::(), + rand::random::(), + ), + } + } + + pub fn sample() -> Self { + Self { + flexi_dag_accumulator_info: AccumulatorInfo::sample(), } } } diff --git a/types/src/sync_status.rs b/types/src/sync_status.rs index 5ad32c6169..1fb3b97e9e 100644 --- a/types/src/sync_status.rs +++ b/types/src/sync_status.rs @@ -5,6 +5,7 @@ use crate::block::BlockIdAndNumber; use crate::startup_info::ChainStatus; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator}; use starcoin_uint::U256; #[derive(Eq, PartialEq, Deserialize, Serialize, Clone, Debug, JsonSchema)] pub enum SyncState { @@ -38,15 +39,28 @@ impl SyncState { pub struct SyncStatus { chain_status: ChainStatus, state: SyncState, + dag_accumulator_info: Option, } pub const NEARLY_SYNCED_BLOCKS: u64 = 24; impl SyncStatus { - pub fn new(chain_status: ChainStatus) -> Self { + pub fn new(chain_status: ChainStatus, dag_accumulator_info: Option) -> Self { Self { chain_status, state: SyncState::Prepare, + dag_accumulator_info, + } + } + + pub fn new_with_dag_accumulator( + chain_status: ChainStatus, + dag_accumulator_info: AccumulatorInfo, + ) -> Self { + Self { + chain_status, + state: SyncState::Prepare, + dag_accumulator_info: Some(dag_accumulator_info), } } @@ -69,10 +83,18 @@ impl SyncStatus { false } + pub fn update_dag_accumulator_info(&mut self, dag_accumulator_info: Option) { + self.dag_accumulator_info = dag_accumulator_info; + } + pub fn sync_status(&self) -> &SyncState { &self.state } + pub fn dag_accumulator_info(&self) -> &Option { + &self.dag_accumulator_info + } + pub fn chain_status(&self) -> &ChainStatus { &self.chain_status } @@ -89,13 +111,11 @@ impl SyncStatus { target, total_difficulty, } => { - if target.number() < self.chain_status.head().number() { + let max_header_number = self.chain_status.head().number(); + if target.number() < max_header_number { false } else { - target - .number - .saturating_sub(self.chain_status.head().number()) - <= NEARLY_SYNCED_BLOCKS + target.number.saturating_sub(max_header_number) <= NEARLY_SYNCED_BLOCKS || self.chain_status.total_difficulty() >= *total_difficulty } } diff --git a/types/src/system_events.rs b/types/src/system_events.rs index 0a84fe1a2d..d8fd2ce137 100644 --- a/types/src/system_events.rs +++ b/types/src/system_events.rs @@ -10,14 +10,18 @@ use starcoin_crypto::HashValue; use starcoin_vm_types::genesis_config::ConsensusStrategy; use std::sync::Arc; #[derive(Clone, Debug)] -pub struct NewHeadBlock(pub Arc); +pub struct NewHeadBlock( + pub Arc, + pub Option>, + pub Option>, +); /// may be uncle block #[derive(Clone, Debug)] -pub struct NewBranch(pub Arc); +pub struct NewBranch(pub Arc, pub Option>); #[derive(Clone, Debug)] -pub struct MinedBlock(pub Arc); +pub struct MinedBlock(pub Arc, pub Option>); ///Fire this event on System start and all service is init. #[derive(Clone, Debug)] diff --git a/vm/compiler/Cargo.toml b/vm/compiler/Cargo.toml index 4665969ede..7eb7dde473 100644 --- a/vm/compiler/Cargo.toml +++ b/vm/compiler/Cargo.toml @@ -24,7 +24,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-move-compiler" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/dev/Cargo.toml b/vm/dev/Cargo.toml index a2a93e9180..33fbf4bf84 100644 --- a/vm/dev/Cargo.toml +++ b/vm/dev/Cargo.toml @@ -21,7 +21,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-dev" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/gas-algebra-ext/Cargo.toml b/vm/gas-algebra-ext/Cargo.toml index ef7dee9696..b702bf116b 100644 --- a/vm/gas-algebra-ext/Cargo.toml +++ b/vm/gas-algebra-ext/Cargo.toml @@ -16,7 +16,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-gas-algebra-ext" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/move-coverage/Cargo.toml b/vm/move-coverage/Cargo.toml index 8b320b8bce..54d25b4713 100644 --- a/vm/move-coverage/Cargo.toml +++ b/vm/move-coverage/Cargo.toml @@ -21,7 +21,7 @@ edition = { workspace = true } license = { workspace = true } name = "move-coverage" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/move-explain/Cargo.toml b/vm/move-explain/Cargo.toml index 7e5d9857b4..3539fd50c4 100644 --- a/vm/move-explain/Cargo.toml +++ b/vm/move-explain/Cargo.toml @@ -16,5 +16,5 @@ license = { workspace = true } name = "starcoin-move-explain" publish = { workspace = true } repository = { workspace = true } -version = "1.13.7" +version = "1.13.5" rust-version = { workspace = true } diff --git a/vm/move-package-manager/Cargo.toml b/vm/move-package-manager/Cargo.toml index 5129c3bfbd..fa7606377b 100644 --- a/vm/move-package-manager/Cargo.toml +++ b/vm/move-package-manager/Cargo.toml @@ -67,5 +67,5 @@ license = { workspace = true } name = "move-package-manager" publish = { workspace = true } repository = { workspace = true } -version = "1.13.7" +version = "1.13.5" rust-version = { workspace = true } diff --git a/vm/move-package-manager/src/release.rs b/vm/move-package-manager/src/release.rs index a1f47792d7..0b13e227a5 100644 --- a/vm/move-package-manager/src/release.rs +++ b/vm/move-package-manager/src/release.rs @@ -23,7 +23,7 @@ pub const DEFAULT_RELEASE_DIR: &str = "release"; pub struct Release { #[clap(name = "move-version", long = "move-version", default_value="6", possible_values=&["5", "6"])] /// specify the move lang version for the release. - /// currently, only v6 are supported. + /// currently, only v5, v6 are supported. language_version: u8, #[clap(name="release-dir", long, parse(from_os_str), default_value=DEFAULT_RELEASE_DIR)] @@ -78,6 +78,7 @@ pub fn handle_release( for m in pkg.root_compiled_units.as_slice() { let m = module(&m.unit)?; println!("\t {}", m.self_id()); + // XXX FIXME YSG, mpm release let code = if language_version as u32 == VERSION_4 { ModuleBytecodeDowngrader::to_v4(m)? } else { diff --git a/vm/move-prover/Cargo.toml b/vm/move-prover/Cargo.toml index 55d63315bd..18bb9d37f7 100644 --- a/vm/move-prover/Cargo.toml +++ b/vm/move-prover/Cargo.toml @@ -35,7 +35,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-move-prover" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/natives/Cargo.toml b/vm/natives/Cargo.toml index 9b05d70c47..3d0696061d 100644 --- a/vm/natives/Cargo.toml +++ b/vm/natives/Cargo.toml @@ -30,7 +30,7 @@ testing = ["move-stdlib/testing"] authors = { workspace = true } edition = { workspace = true } name = "starcoin-natives" -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } license = { workspace = true } publish = { workspace = true } diff --git a/vm/resource-viewer/Cargo.toml b/vm/resource-viewer/Cargo.toml index 46f1c9c848..848a0e3755 100644 --- a/vm/resource-viewer/Cargo.toml +++ b/vm/resource-viewer/Cargo.toml @@ -13,7 +13,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-resource-viewer" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-gas/Cargo.toml b/vm/starcoin-gas/Cargo.toml index 3ebace8295..c1e7983495 100644 --- a/vm/starcoin-gas/Cargo.toml +++ b/vm/starcoin-gas/Cargo.toml @@ -16,7 +16,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-gas" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-transactional-test-harness/Cargo.toml b/vm/starcoin-transactional-test-harness/Cargo.toml index a93426a4a0..8c0c9f1868 100644 --- a/vm/starcoin-transactional-test-harness/Cargo.toml +++ b/vm/starcoin-transactional-test-harness/Cargo.toml @@ -70,7 +70,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-transactional-test-harness" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-transactional-test-harness/src/fork_chain.rs b/vm/starcoin-transactional-test-harness/src/fork_chain.rs index 9d0dda112d..7431565fa8 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_chain.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_chain.rs @@ -114,7 +114,11 @@ impl ForkBlockChain { }) } - pub fn add_new_block(&mut self, mut block: Block) -> Result<()> { + pub fn add_new_block( + &mut self, + mut block: Block, + dag_parent: Option>, + ) -> Result<()> { block.header = block.header().as_builder().build(); let block_accumulator = MerkleAccumulator::new_empty( @@ -132,7 +136,7 @@ impl ForkBlockChain { self.number_hash_map .insert(self.current_number, block.header().id()); self.status = Some(ChainStatusWithBlock { - status: ChainStatus::new(block.header().clone(), block_info.clone()), + status: ChainStatus::new(block.header().clone(), block_info.clone(), dag_parent), head: block.clone(), }); self.storage.save_block_info(block_info)?; @@ -143,7 +147,7 @@ impl ForkBlockChain { pub fn add_new_txn(&mut self, txn: Transaction, output: TransactionOutput) -> Result<()> { let txn_hash = txn.id(); let state_root = *self.state_root.lock().unwrap(); - let (_, _, events, gas_used, status) = output.into_inner(); + let (_, events, gas_used, status) = output.into_inner(); let status = status .status() .expect("TransactionStatus at here must been KeptVMStatus"); @@ -198,6 +202,7 @@ impl ChainApi for MockChainApi { status.head.header().chain_id(), HashValue::random(), status.status, + None, ))), None => match client { Some(client) => client.info().await.map_err(|e| anyhow!("{}", e)), diff --git a/vm/starcoin-transactional-test-harness/src/fork_state.rs b/vm/starcoin-transactional-test-harness/src/fork_state.rs index dec9571d5d..6266115f3b 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_state.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_state.rs @@ -21,7 +21,7 @@ use starcoin_types::access_path::AccessPath; use starcoin_types::account_state::AccountState; use starcoin_types::state_set::AccountStateSet; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; +use starcoin_vm_types::state_store::table::TableHandle; use tokio::runtime::Runtime; pub struct MockStateNodeStore { @@ -72,10 +72,6 @@ impl StateNodeStore for MockStateNodeStore { let batch = CodecWriteBatch::new_puts(nodes.into_iter().collect()); self.local_storage.write_batch(batch) } - - fn get_table_info(&self, _address: AccountAddress) -> Result> { - Ok(None) - } } #[derive(Clone)] @@ -162,9 +158,4 @@ impl ChainStateAsyncService for MockChainStateAsyncService { let reader = self.state_db().fork_at(state_root); reader.get_with_table_item_proof(&handle, &key) } - - async fn get_table_info(self, address: AccountAddress) -> Result> { - let reader = self.state_db().fork(); - reader.get_table_info(address) - } } diff --git a/vm/starcoin-transactional-test-harness/src/lib.rs b/vm/starcoin-transactional-test-harness/src/lib.rs index 6e023aabfe..5cf3112902 100644 --- a/vm/starcoin-transactional-test-harness/src/lib.rs +++ b/vm/starcoin-transactional-test-harness/src/lib.rs @@ -646,7 +646,7 @@ impl<'a> StarcoinTestAdapter<'a> { TransactionStatus::Keep(kept_vm_status) => match kept_vm_status { KeptVMStatus::Executed => { self.context - .apply_write_set(output.clone().into_inner().1)?; + .apply_write_set(output.clone().into_inner().0)?; } _ => { bail!("Failed to execute transaction. VMStatus: {}", status) @@ -655,9 +655,6 @@ impl<'a> StarcoinTestAdapter<'a> { TransactionStatus::Discard(_) => { bail!("Transaction discarded. VMStatus: {}", status) } - TransactionStatus::Retry => { - bail!("Transaction Retry never happen") - } } let mut chain = self.context.chain.lock().unwrap(); chain.add_new_txn(Transaction::BlockMetadata(meta), output)?; @@ -684,7 +681,7 @@ impl<'a> StarcoinTestAdapter<'a> { match output.status() { TransactionStatus::Keep(_kept_vm_status) => { self.context - .apply_write_set(output.clone().into_inner().1)?; + .apply_write_set(output.clone().into_inner().0)?; let mut chain = self.context.chain.lock().unwrap(); chain.add_new_txn( Transaction::UserTransaction(signed_txn.clone()), @@ -692,7 +689,6 @@ impl<'a> StarcoinTestAdapter<'a> { )?; } TransactionStatus::Discard(_) => {} - TransactionStatus::Retry => {} } let payload = decode_txn_payload(&self.context.storage, signed_txn.payload())?; let mut txn_view: SignedUserTransactionView = signed_txn.try_into()?; @@ -873,7 +869,7 @@ impl<'a> StarcoinTestAdapter<'a> { ); let new_block = Block::new(block_header, block_body); let mut chain = self.context.chain.lock().unwrap(); - chain.add_new_block(new_block)?; + chain.add_new_block(new_block, None)?; Ok((None, Some(serde_json::to_value(&new_block_meta)?))) } diff --git a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp index 6d1cfb7a18..909fa3b096 100644 --- a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp +++ b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp @@ -2,7 +2,7 @@ processed 10 tasks task 5 'run'. lines 11-19: { - "gas_used": 11125, + "gas_used": 15127, "status": "Executed" } diff --git a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp index 878d67d4d6..aaf0bb0de7 100644 --- a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp +++ b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp @@ -2,6 +2,6 @@ processed 4 tasks task 3 'run'. lines 7-14: { - "gas_used": 8712, + "gas_used": 10461, "status": "Executed" } diff --git a/vm/stdlib/Cargo.toml b/vm/stdlib/Cargo.toml index 745e5af3c3..fe6032b018 100644 --- a/vm/stdlib/Cargo.toml +++ b/vm/stdlib/Cargo.toml @@ -29,7 +29,7 @@ edition = { workspace = true } license = { workspace = true } name = "stdlib" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/stdlib/compiled/12/11-12/stdlib.blob b/vm/stdlib/compiled/12/11-12/stdlib.blob index e17900e644114cff80e894966b33de13ad05eaf3..65e03ac824b9d69953234f6670cb1ad5c4d662e0 100644 GIT binary patch delta 21897 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 N1;)}|S-^E0{|B|gUu*yX diff --git a/vm/stdlib/compiled/12/11-12/stdlib/008_Vector.mv b/vm/stdlib/compiled/12/11-12/stdlib/008_Vector.mv index fca0c13f9fa22089f35b632ccb13ea8f3c0c1427..ccd5d44b8211f0b9f46c6068eb77bcec872b8cba 100644 GIT binary patch delta 394 zcmYk2u}T9$5Qb-FcklLg<}fiAPcfB|gn*>6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFPMa@gAH2|XM+wx&KRM1N{RFhPb&7^wpUtJ?040;u7IIoUAORiSrY_{F?$ny)5}OUZ58 zR9_|wOUqj9pg)nGn!1**H?c0xZ6%)L%GXDBb7wg&3Rnt9sn}rWX>L#5aWcoYjY~bX h>0agCnKH4b9ulmnEhhm+r7SL_%C5Lf2MWJ01b;C>E3*Iq diff --git a/vm/stdlib/compiled/12/11-12/stdlib/010_ACL.mv b/vm/stdlib/compiled/12/11-12/stdlib/010_ACL.mv index 773134f2eea099ed527223f864d498f52f79dc00..af6907e2302b8894312dfe7b61c5f5e32dfb7527 100644 GIT binary patch delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#skE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u diff --git a/vm/stdlib/compiled/12/11-12/stdlib/012_Math.mv b/vm/stdlib/compiled/12/11-12/stdlib/012_Math.mv index 034dad6d3ebb46c0a07663fc6820bdfad530c1f9..2d856ff65174a6b7437c74f959fea73593244c12 100644 GIT binary patch delta 231 zcmWlSJxT*n6ot=u_r5!Ceh{?`vjU5RpyDjR*2aLnN-#xqU5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ delta 190 zcmWm6F%CgN5C-7+cV>6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H diff --git a/vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv b/vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv index d66fd2976727d10389ab808b938c050b7740b0c9..d58d5363cc266a5cbb3bec62584544ee56c164b7 100644 GIT binary patch delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U0$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC diff --git a/vm/stdlib/compiled/12/11-12/stdlib/021_VMConfig.mv b/vm/stdlib/compiled/12/11-12/stdlib/021_VMConfig.mv index d4a4038b71b1b7f39be486752cba792c6aef31b6..ec731ac18d8ecbb2a4900f0aad7b805f55fbfcb7 100644 GIT binary patch literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy literal 3967 zcmds4&2HpG5bhuUjk}%9OtP8DW|v*K01_v}qCJ2V6a?A>5(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw diff --git a/vm/stdlib/compiled/12/11-12/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/12/11-12/stdlib/036_Authenticator.mv index c5a74c50726f9630bc63c08e50f2b5271cbbcc8f..7fff119e122a9a7d9b41cf70cc56ef926141b43c 100644 GIT binary patch delta 469 zcmW+yO-h_W5Ur}NuAct;=KIV?AZ9iy5+Z~!kVUUu3JG|EVT^+sbw(y*M9^gxSx)8x z@d}cq8;{@ta)6Laq&{oWyw~;mb-lWizcRcV{EGqr5g|}uQ|B3IVFBaEwFq_Y3SN`Ji>+$Gd{P}csg}0vyBnmi$P43y~Y~$Vp{19lH8{0Zk8p**n zzp?{oI;P85140A{n-vsvu)$)zV{H~%?a{`UV%kI*BNdKDRQX&lld(Bs6G;<;mkbV7 z08P?}0R6WCYDhL%oyZhQ*lqr~bU+GbiwCwJGfPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN diff --git a/vm/stdlib/compiled/12/11-12/stdlib/037_Account.mv b/vm/stdlib/compiled/12/11-12/stdlib/037_Account.mv index 1a3465588cf2f4a091e256a0ecd694ebae2ea0ee..dfe90a434e661c2ecd31a9e2657637e54a2087a2 100644 GIT binary patch delta 2832 zcmZ8j%TpZZ5&yd9`{wn{&dw~eJQrBR0%CXxB;eyA&;#_eY#F_gYzz`Xieyj#S(ama zZO2J$%WdZ3`iCAWB%eXx)D*i70HKSy`}#mf_>TifsvuBULJB2p;RsiF!WUWuA}2z&>mU*rNw#>6SmTLV24DMYnM zL?aPR^lYYs7NT2ip|vD&{hA{j#{nVKsstNc*Ab#k07y})VLMtvbf6?gr(!H4+3B*7 z$bHCi-40{LW$dY8ujdQV$ARxhQ5B+J6PdE!0K;IwqC+{x;gE48&v+oBFh*m7F^}I5 z7Kl95z~p!hCs=#Zi-a&Z5#G*$QPSl*8tYv$OLpYrfoM|IC%kWZ(;2gFK@p6&il@5A6-%0SQrWBWIHMme_ zn-{wnFYRNz++88O($hrwU+rc3S|8)`e#YzlgnDa0f|U2$+lBiRs=f5KJruYzY=Ow_ z9sm&b1IrS*w_ubQ566ZH-#$oed}N{U-yzER=^-109vml^`rQiSd%&h|zywJ3z5%G( zvPvGD58!Z0q8|cK>vA8#d^-sFGoT~&F;I=1pF?^Cg!%<|N5OV}3CGF4{DcLpPgy{I z#&qs6%#+LDb8eqv-%o*Fql za%RLCJQtlylJw=YoW|*7T1Z>cSJLz8tLf6@()7~I((J`(Y3^cjX|OZBJaR3$7A?;% z&n!nhcpM0SHYz2)Q>$@u(>znI4cXqN-Z|=#*-d_NU*_JM5o4S+_ zckXO#u5Yib-nzBDzO$2kqla_B?%SIy_qVt1ZEZYQ&$@!e?6*Pw$rr(|p-HX3d4Knj zy|%Tzz4g$$|6u3#%G&D9yIDtgshHc{y1Tyl%#wX?eRE^?cD57FWlzIHPreUVp?bs3 zQ^@T;zOEy1gB3>S4IP7w0cf&`8)@_z??=adwX7p`@ zyUJm35$~#dK6(gmD(cEzr4cqEU@C|Dxfv_XzKuSCW7$sp$R3*1B3{V8i~Ek83~ai~ zdw&6%Frd~tqFWBAat=b1R$nFgdiV*y&s(cmks%|ou4yZ*jA;&fTcx0 zAT2Sv`BBX-kf(fEx=K4rHqhy_gpnVy1&v=;$wO%{~2-RxSDOUlgRC`>PQafxj$vd@#AIn!sC>y7J@w$kj! zkFWIA+#}&t`pQwu3xU~BnL4JQikZ@HfPRD7zmt|K)yfQ$<&lCKQrN@R3#{I+$};cM zSJJ#7p`%qMa|bC44+p3o78fC2m=n{&wBaD_D+{bpm_sw58(u7K%OEi!AU`Bpoo1jBg&FzU)2VsA-=%_kPCnu0UFMhHtb#rO-M5;wK9`9 zRXaGbK(epVus}eAIx7N?kG0*@JRbPIR~G$&I!+F7x)#*Q_xFN| z+p08fo}=T{J{ofBQ>bDmrU|bF?D!75?O`|Znh#yFQ&qGBPL5_%jWOGIB9U~-A$qIx zeL3!{@n)ykBWKhR<_09qNjC?!|Gza>ksC(J>REN3WfaSRnbG!mue_peJk#@W+$na; zHMPNLAj;ukz8v-KcHPY4 z+>5fKIA3F&rfU=B5^5RUSR#D7A?7fzATx#Z{{eOVOqpkww-ZT8-{LjF<3`^-S`*oq Wt!+_>wtHQZu92C_zG*FED*g*={3fsf delta 3384 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 delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(dS>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ diff --git a/vm/stdlib/compiled/12/11-12/stdlib/044_Collection.mv b/vm/stdlib/compiled/12/11-12/stdlib/044_Collection.mv index 306c01fc8c236d304380ad8666cf6810adb50368..14cb6885c15c02e39d50fe39f2f4d45ea04ee280 100644 GIT binary patch delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~ z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk diff --git a/vm/stdlib/compiled/12/11-12/stdlib/045_Collection2.mv b/vm/stdlib/compiled/12/11-12/stdlib/045_Collection2.mv index ab314639e15c5fc1a7e499593dba7915fa0d0cbf..18a0ceb49462ba1dd03873cf1c5d36824700f1e1 100644 GIT binary patch delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfNETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp diff --git a/vm/stdlib/compiled/12/11-12/stdlib/046_Compare.mv b/vm/stdlib/compiled/12/11-12/stdlib/046_Compare.mv index e3e7ef4a6ae9a2ad703c9dd09d6d19fe9d1ae49a..86aec6c01b49cdb2ae53ccb26ea7085154096dc5 100644 GIT binary patch delta 284 zcmXw!y-Ec!5QXRDXOhe=Vqqyr!0Hwjw+dJaf`x^R)_PeKQCF7>mcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= delta 234 zcmYk0JqiLb5Ju;l{JGgJECdThOeI=+1Gi8tRICIWZ(;2fmSDSJVPoM%Jc@-^a1s#A zgf|~~$&iQn=;LkpE-eu`5Gx*;@x-mRoh!Qbj-q;^RU#opoS;~sA%`Zx3fTIz!K@Te zNS7dJF8!umPUZRkf(%G^k!R|irv)nOu|OPP?`!m`!o#~dp0Td%CVQ9t3-j!~cw0xu Y2e^3RSunaJM}f8cEoTHazs!N@3%|z|Y5)KL diff --git a/vm/stdlib/compiled/12/11-12/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/12/11-12/stdlib/051_EVMAddress.mv index 9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7..486a2ad6212e59a89d0807ca7c0d0c46a3600c74 100644 GIT binary patch delta 249 zcmWlUy-Gtt6ok+0o;`c_4@80%UT3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;FI|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS`6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFPMa@gAH2|XM+wx&KRM1N{RFhPb&7^wpUtJ?040;u7IIoUAORiSrY_{F?$ny)5}OUZ58 zR9_|wOUqj9pg)nGn!1**H?c0xZ6%)L%GXDBb7wg&3Rnt9sn}rWX>L#5aWcoYjY~bX h>0agCnKH4b9ulmnEhhm+r7SL_%C5Lf2MWJ01b;C>E3*Iq diff --git a/vm/stdlib/compiled/12/stdlib/010_ACL.mv b/vm/stdlib/compiled/12/stdlib/010_ACL.mv index 773134f2eea099ed527223f864d498f52f79dc00..af6907e2302b8894312dfe7b61c5f5e32dfb7527 100644 GIT binary patch delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#skE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u diff --git a/vm/stdlib/compiled/12/stdlib/012_Math.mv b/vm/stdlib/compiled/12/stdlib/012_Math.mv index 034dad6d3ebb46c0a07663fc6820bdfad530c1f9..2d856ff65174a6b7437c74f959fea73593244c12 100644 GIT binary patch delta 231 zcmWlSJxT*n6ot=u_r5!Ceh{?`vjU5RpyDjR*2aLnN-#xqU5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ delta 190 zcmWm6F%CgN5C-7+cV>6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H diff --git a/vm/stdlib/compiled/12/stdlib/014_BCS.mv b/vm/stdlib/compiled/12/stdlib/014_BCS.mv index d66fd2976727d10389ab808b938c050b7740b0c9..d58d5363cc266a5cbb3bec62584544ee56c164b7 100644 GIT binary patch delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U0$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC diff --git a/vm/stdlib/compiled/12/stdlib/021_VMConfig.mv b/vm/stdlib/compiled/12/stdlib/021_VMConfig.mv index d4a4038b71b1b7f39be486752cba792c6aef31b6..ec731ac18d8ecbb2a4900f0aad7b805f55fbfcb7 100644 GIT binary patch literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy literal 3967 zcmds4&2HpG5bhuUjk}%9OtP8DW|v*K01_v}qCJ2V6a?A>5(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw diff --git a/vm/stdlib/compiled/12/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/12/stdlib/036_Authenticator.mv index c5a74c50726f9630bc63c08e50f2b5271cbbcc8f..7fff119e122a9a7d9b41cf70cc56ef926141b43c 100644 GIT binary patch delta 469 zcmW+yO-h_W5Ur}NuAct;=KIV?AZ9iy5+Z~!kVUUu3JG|EVT^+sbw(y*M9^gxSx)8x z@d}cq8;{@ta)6Laq&{oWyw~;mb-lWizcRcV{EGqr5g|}uQ|B3IVFBaEwFq_Y3SN`Ji>+$Gd{P}csg}0vyBnmi$P43y~Y~$Vp{19lH8{0Zk8p**n zzp?{oI;P85140A{n-vsvu)$)zV{H~%?a{`UV%kI*BNdKDRQX&lld(Bs6G;<;mkbV7 z08P?}0R6WCYDhL%oyZhQ*lqr~bU+GbiwCwJGfPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN diff --git a/vm/stdlib/compiled/12/stdlib/037_Account.mv b/vm/stdlib/compiled/12/stdlib/037_Account.mv index 1a3465588cf2f4a091e256a0ecd694ebae2ea0ee..dfe90a434e661c2ecd31a9e2657637e54a2087a2 100644 GIT binary patch delta 2832 zcmZ8j%TpZZ5&yd9`{wn{&dw~eJQrBR0%CXxB;eyA&;#_eY#F_gYzz`Xieyj#S(ama zZO2J$%WdZ3`iCAWB%eXx)D*i70HKSy`}#mf_>TifsvuBULJB2p;RsiF!WUWuA}2z&>mU*rNw#>6SmTLV24DMYnM zL?aPR^lYYs7NT2ip|vD&{hA{j#{nVKsstNc*Ab#k07y})VLMtvbf6?gr(!H4+3B*7 z$bHCi-40{LW$dY8ujdQV$ARxhQ5B+J6PdE!0K;IwqC+{x;gE48&v+oBFh*m7F^}I5 z7Kl95z~p!hCs=#Zi-a&Z5#G*$QPSl*8tYv$OLpYrfoM|IC%kWZ(;2gFK@p6&il@5A6-%0SQrWBWIHMme_ zn-{wnFYRNz++88O($hrwU+rc3S|8)`e#YzlgnDa0f|U2$+lBiRs=f5KJruYzY=Ow_ z9sm&b1IrS*w_ubQ566ZH-#$oed}N{U-yzER=^-109vml^`rQiSd%&h|zywJ3z5%G( zvPvGD58!Z0q8|cK>vA8#d^-sFGoT~&F;I=1pF?^Cg!%<|N5OV}3CGF4{DcLpPgy{I z#&qs6%#+LDb8eqv-%o*Fql za%RLCJQtlylJw=YoW|*7T1Z>cSJLz8tLf6@()7~I((J`(Y3^cjX|OZBJaR3$7A?;% z&n!nhcpM0SHYz2)Q>$@u(>znI4cXqN-Z|=#*-d_NU*_JM5o4S+_ zckXO#u5Yib-nzBDzO$2kqla_B?%SIy_qVt1ZEZYQ&$@!e?6*Pw$rr(|p-HX3d4Knj zy|%Tzz4g$$|6u3#%G&D9yIDtgshHc{y1Tyl%#wX?eRE^?cD57FWlzIHPreUVp?bs3 zQ^@T;zOEy1gB3>S4IP7w0cf&`8)@_z??=adwX7p`@ zyUJm35$~#dK6(gmD(cEzr4cqEU@C|Dxfv_XzKuSCW7$sp$R3*1B3{V8i~Ek83~ai~ zdw&6%Frd~tqFWBAat=b1R$nFgdiV*y&s(cmks%|ou4yZ*jA;&fTcx0 zAT2Sv`BBX-kf(fEx=K4rHqhy_gpnVy1&v=;$wO%{~2-RxSDOUlgRC`>PQafxj$vd@#AIn!sC>y7J@w$kj! zkFWIA+#}&t`pQwu3xU~BnL4JQikZ@HfPRD7zmt|K)yfQ$<&lCKQrN@R3#{I+$};cM zSJJ#7p`%qMa|bC44+p3o78fC2m=n{&wBaD_D+{bpm_sw58(u7K%OEi!AU`Bpoo1jBg&FzU)2VsA-=%_kPCnu0UFMhHtb#rO-M5;wK9`9 zRXaGbK(epVus}eAIx7N?kG0*@JRbPIR~G$&I!+F7x)#*Q_xFN| z+p08fo}=T{J{ofBQ>bDmrU|bF?D!75?O`|Znh#yFQ&qGBPL5_%jWOGIB9U~-A$qIx zeL3!{@n)ykBWKhR<_09qNjC?!|Gza>ksC(J>REN3WfaSRnbG!mue_peJk#@W+$na; zHMPNLAj;ukz8v-KcHPY4 z+>5fKIA3F&rfU=B5^5RUSR#D7A?7fzATx#Z{{eOVOqpkww-ZT8-{LjF<3`^-S`*oq Wt!+_>wtHQZu92C_zG*FED*g*={3fsf delta 3384 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 delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(dS>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ diff --git a/vm/stdlib/compiled/12/stdlib/044_Collection.mv b/vm/stdlib/compiled/12/stdlib/044_Collection.mv index 306c01fc8c236d304380ad8666cf6810adb50368..14cb6885c15c02e39d50fe39f2f4d45ea04ee280 100644 GIT binary patch delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~ z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk diff --git a/vm/stdlib/compiled/12/stdlib/045_Collection2.mv b/vm/stdlib/compiled/12/stdlib/045_Collection2.mv index ab314639e15c5fc1a7e499593dba7915fa0d0cbf..18a0ceb49462ba1dd03873cf1c5d36824700f1e1 100644 GIT binary patch delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfNETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp diff --git a/vm/stdlib/compiled/12/stdlib/046_Compare.mv b/vm/stdlib/compiled/12/stdlib/046_Compare.mv index e3e7ef4a6ae9a2ad703c9dd09d6d19fe9d1ae49a..86aec6c01b49cdb2ae53ccb26ea7085154096dc5 100644 GIT binary patch delta 284 zcmXw!y-Ec!5QXRDXOhe=Vqqyr!0Hwjw+dJaf`x^R)_PeKQCF7>mcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= delta 234 zcmYk0JqiLb5Ju;l{JGgJECdThOeI=+1Gi8tRICIWZ(;2fmSDSJVPoM%Jc@-^a1s#A zgf|~~$&iQn=;LkpE-eu`5Gx*;@x-mRoh!Qbj-q;^RU#opoS;~sA%`Zx3fTIz!K@Te zNS7dJF8!umPUZRkf(%G^k!R|irv)nOu|OPP?`!m`!o#~dp0Td%CVQ9t3-j!~cw0xu Y2e^3RSunaJM}f8cEoTHazs!N@3%|z|Y5)KL diff --git a/vm/stdlib/compiled/12/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/12/stdlib/051_EVMAddress.mv index 9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7..486a2ad6212e59a89d0807ca7c0d0c46a3600c74 100644 GIT binary patch delta 249 zcmWlUy-Gtt6ok+0o;`c_4@80%UT3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;FI|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS`tuZ%t;xHUCU0g`X=VWcnTQEj delta 62 zcmV-E0Kxz2O@vVb06DP&G!F6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFPMa@gAH2|XM+wx&KRM1N{RFhPb&7^wpUtJ?040;u7IIoUAORiSrY_{F?$ny)5}OUZ58 zR9_|wOUqj9pg)nGn!1**H?c0xZ6%)L%GXDBb7wg&3Rnt9sn}rWX>L#5aWcoYjY~bX h>0agCnKH4b9ulmnEhhm+r7SL_%C5Lf2MWJ01b;C>E3*Iq diff --git a/vm/stdlib/compiled/latest/stdlib/010_ACL.mv b/vm/stdlib/compiled/latest/stdlib/010_ACL.mv index 773134f2eea099ed527223f864d498f52f79dc00..af6907e2302b8894312dfe7b61c5f5e32dfb7527 100644 GIT binary patch delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#skE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u diff --git a/vm/stdlib/compiled/latest/stdlib/012_Math.mv b/vm/stdlib/compiled/latest/stdlib/012_Math.mv index 034dad6d3ebb46c0a07663fc6820bdfad530c1f9..2d856ff65174a6b7437c74f959fea73593244c12 100644 GIT binary patch delta 231 zcmWlSJxT*n6ot=u_r5!Ceh{?`vjU5RpyDjR*2aLnN-#xqU5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ delta 190 zcmWm6F%CgN5C-7+cV>6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H diff --git a/vm/stdlib/compiled/latest/stdlib/014_BCS.mv b/vm/stdlib/compiled/latest/stdlib/014_BCS.mv index d66fd2976727d10389ab808b938c050b7740b0c9..d58d5363cc266a5cbb3bec62584544ee56c164b7 100644 GIT binary patch delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U0$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC diff --git a/vm/stdlib/compiled/latest/stdlib/021_VMConfig.mv b/vm/stdlib/compiled/latest/stdlib/021_VMConfig.mv index d4a4038b71b1b7f39be486752cba792c6aef31b6..ec731ac18d8ecbb2a4900f0aad7b805f55fbfcb7 100644 GIT binary patch literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy literal 3967 zcmds4&2HpG5bhuUjk}%9OtP8DW|v*K01_v}qCJ2V6a?A>5(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw diff --git a/vm/stdlib/compiled/latest/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/latest/stdlib/036_Authenticator.mv index c5a74c50726f9630bc63c08e50f2b5271cbbcc8f..7fff119e122a9a7d9b41cf70cc56ef926141b43c 100644 GIT binary patch delta 469 zcmW+yO-h_W5Ur}NuAct;=KIV?AZ9iy5+Z~!kVUUu3JG|EVT^+sbw(y*M9^gxSx)8x z@d}cq8;{@ta)6Laq&{oWyw~;mb-lWizcRcV{EGqr5g|}uQ|B3IVFBaEwFq_Y3SN`Ji>+$Gd{P}csg}0vyBnmi$P43y~Y~$Vp{19lH8{0Zk8p**n zzp?{oI;P85140A{n-vsvu)$)zV{H~%?a{`UV%kI*BNdKDRQX&lld(Bs6G;<;mkbV7 z08P?}0R6WCYDhL%oyZhQ*lqr~bU+GbiwCwJGfPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN diff --git a/vm/stdlib/compiled/latest/stdlib/037_Account.mv b/vm/stdlib/compiled/latest/stdlib/037_Account.mv index 1a3465588cf2f4a091e256a0ecd694ebae2ea0ee..dfe90a434e661c2ecd31a9e2657637e54a2087a2 100644 GIT binary patch delta 2832 zcmZ8j%TpZZ5&yd9`{wn{&dw~eJQrBR0%CXxB;eyA&;#_eY#F_gYzz`Xieyj#S(ama zZO2J$%WdZ3`iCAWB%eXx)D*i70HKSy`}#mf_>TifsvuBULJB2p;RsiF!WUWuA}2z&>mU*rNw#>6SmTLV24DMYnM zL?aPR^lYYs7NT2ip|vD&{hA{j#{nVKsstNc*Ab#k07y})VLMtvbf6?gr(!H4+3B*7 z$bHCi-40{LW$dY8ujdQV$ARxhQ5B+J6PdE!0K;IwqC+{x;gE48&v+oBFh*m7F^}I5 z7Kl95z~p!hCs=#Zi-a&Z5#G*$QPSl*8tYv$OLpYrfoM|IC%kWZ(;2gFK@p6&il@5A6-%0SQrWBWIHMme_ zn-{wnFYRNz++88O($hrwU+rc3S|8)`e#YzlgnDa0f|U2$+lBiRs=f5KJruYzY=Ow_ z9sm&b1IrS*w_ubQ566ZH-#$oed}N{U-yzER=^-109vml^`rQiSd%&h|zywJ3z5%G( zvPvGD58!Z0q8|cK>vA8#d^-sFGoT~&F;I=1pF?^Cg!%<|N5OV}3CGF4{DcLpPgy{I z#&qs6%#+LDb8eqv-%o*Fql za%RLCJQtlylJw=YoW|*7T1Z>cSJLz8tLf6@()7~I((J`(Y3^cjX|OZBJaR3$7A?;% z&n!nhcpM0SHYz2)Q>$@u(>znI4cXqN-Z|=#*-d_NU*_JM5o4S+_ zckXO#u5Yib-nzBDzO$2kqla_B?%SIy_qVt1ZEZYQ&$@!e?6*Pw$rr(|p-HX3d4Knj zy|%Tzz4g$$|6u3#%G&D9yIDtgshHc{y1Tyl%#wX?eRE^?cD57FWlzIHPreUVp?bs3 zQ^@T;zOEy1gB3>S4IP7w0cf&`8)@_z??=adwX7p`@ zyUJm35$~#dK6(gmD(cEzr4cqEU@C|Dxfv_XzKuSCW7$sp$R3*1B3{V8i~Ek83~ai~ zdw&6%Frd~tqFWBAat=b1R$nFgdiV*y&s(cmks%|ou4yZ*jA;&fTcx0 zAT2Sv`BBX-kf(fEx=K4rHqhy_gpnVy1&v=;$wO%{~2-RxSDOUlgRC`>PQafxj$vd@#AIn!sC>y7J@w$kj! zkFWIA+#}&t`pQwu3xU~BnL4JQikZ@HfPRD7zmt|K)yfQ$<&lCKQrN@R3#{I+$};cM zSJJ#7p`%qMa|bC44+p3o78fC2m=n{&wBaD_D+{bpm_sw58(u7K%OEi!AU`Bpoo1jBg&FzU)2VsA-=%_kPCnu0UFMhHtb#rO-M5;wK9`9 zRXaGbK(epVus}eAIx7N?kG0*@JRbPIR~G$&I!+F7x)#*Q_xFN| z+p08fo}=T{J{ofBQ>bDmrU|bF?D!75?O`|Znh#yFQ&qGBPL5_%jWOGIB9U~-A$qIx zeL3!{@n)ykBWKhR<_09qNjC?!|Gza>ksC(J>REN3WfaSRnbG!mue_peJk#@W+$na; zHMPNLAj;ukz8v-KcHPY4 z+>5fKIA3F&rfU=B5^5RUSR#D7A?7fzATx#Z{{eOVOqpkww-ZT8-{LjF<3`^-S`*oq Wt!+_>wtHQZu92C_zG*FED*g*={3fsf delta 3384 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 delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(dS>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ diff --git a/vm/stdlib/compiled/latest/stdlib/044_Collection.mv b/vm/stdlib/compiled/latest/stdlib/044_Collection.mv index 306c01fc8c236d304380ad8666cf6810adb50368..14cb6885c15c02e39d50fe39f2f4d45ea04ee280 100644 GIT binary patch delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~ z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk diff --git a/vm/stdlib/compiled/latest/stdlib/045_Collection2.mv b/vm/stdlib/compiled/latest/stdlib/045_Collection2.mv index ab314639e15c5fc1a7e499593dba7915fa0d0cbf..18a0ceb49462ba1dd03873cf1c5d36824700f1e1 100644 GIT binary patch delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfNETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp diff --git a/vm/stdlib/compiled/latest/stdlib/046_Compare.mv b/vm/stdlib/compiled/latest/stdlib/046_Compare.mv index e3e7ef4a6ae9a2ad703c9dd09d6d19fe9d1ae49a..86aec6c01b49cdb2ae53ccb26ea7085154096dc5 100644 GIT binary patch delta 284 zcmXw!y-Ec!5QXRDXOhe=Vqqyr!0Hwjw+dJaf`x^R)_PeKQCF7>mcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= delta 234 zcmYk0JqiLb5Ju;l{JGgJECdThOeI=+1Gi8tRICIWZ(;2fmSDSJVPoM%Jc@-^a1s#A zgf|~~$&iQn=;LkpE-eu`5Gx*;@x-mRoh!Qbj-q;^RU#opoS;~sA%`Zx3fTIz!K@Te zNS7dJF8!umPUZRkf(%G^k!R|irv)nOu|OPP?`!m`!o#~dp0Td%CVQ9t3-j!~cw0xu Y2e^3RSunaJM}f8cEoTHazs!N@3%|z|Y5)KL diff --git a/vm/stdlib/compiled/latest/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/latest/stdlib/051_EVMAddress.mv index 9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7..486a2ad6212e59a89d0807ca7c0d0c46a3600c74 100644 GIT binary patch delta 249 zcmWlUy-Gtt6ok+0o;`c_4@80%UT3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;FI|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS` SignedUserTransaction { - sender.sign_txn(RawUserTransaction::new_with_default_gas_token( - *sender.address(), - seq_num, - TransactionPayload::ScriptFunction(ScriptFunction::new( - ModuleId::new( - core_code_address(), - Identifier::new("TransferScripts").unwrap(), - ), - Identifier::new("peer_to_peer_v2").unwrap(), - vec![stc_type_tag()], - vec![ - bcs_ext::to_bytes(&recipient).unwrap(), - bcs_ext::to_bytes(&amount).unwrap(), - ], - )), - 10000000, - 1, - 1000 + 60 * 60, - net.chain_id(), - )) -} - //this only work for DEV or TEST pub fn create_signed_txn_with_association_account( payload: TransactionPayload, @@ -350,7 +320,10 @@ pub fn create_signed_txn_with_association_account( pub fn build_stdlib_package(net: &ChainNetwork, stdlib_option: StdLibOptions) -> Result { let init_script = match net.genesis_config().stdlib_version { StdlibVersion::Version(1) => build_init_script_v1(net), - _ => build_init_script_v2(net), + StdlibVersion::Version(12) | StdlibVersion::Latest => { + build_init_script_with_function(net, "initialize_v3") + } + _ => build_init_script_with_function(net, "initialize_v2"), }; stdlib_package(stdlib_option, Some(init_script)) } @@ -361,7 +334,10 @@ pub fn build_stdlib_package_with_modules( ) -> Result { let init_script = match net.genesis_config().stdlib_version { StdlibVersion::Version(1) => build_init_script_v1(net), - _ => build_init_script_v2(net), + StdlibVersion::Version(12) | StdlibVersion::Latest => { + build_init_script_with_function(net, "initialize_v3") + } + _ => build_init_script_with_function(net, "initialize_v2"), }; module_to_package(modules, Some(init_script)) } @@ -529,7 +505,7 @@ pub fn build_init_script_v1(net: &ChainNetwork) -> ScriptFunction { ) } -pub fn build_init_script_v2(net: &ChainNetwork) -> ScriptFunction { +pub fn build_init_script_with_function(net: &ChainNetwork, function: &str) -> ScriptFunction { let genesis_config = net.genesis_config(); let chain_id = net.chain_id().id(); let genesis_timestamp = net.genesis_block_parameter().timestamp; @@ -550,147 +526,154 @@ pub fn build_init_script_v2(net: &ChainNetwork) -> ScriptFunction { let native_schedule = bcs_ext::to_bytes(&genesis_config.vm_config.gas_schedule.native_table) .expect("Cannot serialize gas schedule"); + let mut args = vec![ + bcs_ext::to_bytes(&net.genesis_config().stdlib_version.version()).unwrap(), + bcs_ext::to_bytes(&genesis_config.reward_delay).unwrap(), + bcs_ext::to_bytes(&G_TOTAL_STC_AMOUNT.scaling()).unwrap(), + bcs_ext::to_bytes(&genesis_config.pre_mine_amount).unwrap(), + bcs_ext::to_bytes(&genesis_config.time_mint_amount).unwrap(), + bcs_ext::to_bytes(&genesis_config.time_mint_period).unwrap(), + bcs_ext::to_bytes(&genesis_parent_hash.to_vec()).unwrap(), + bcs_ext::to_bytes(&association_auth_key).unwrap(), + bcs_ext::to_bytes(&genesis_auth_key).unwrap(), + bcs_ext::to_bytes(&chain_id).unwrap(), + bcs_ext::to_bytes(&genesis_timestamp).unwrap(), + //consensus config + bcs_ext::to_bytes(&genesis_config.consensus_config.uncle_rate_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.epoch_block_count).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_difficulty_window).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_reward_per_block).unwrap(), + bcs_ext::to_bytes( + &genesis_config + .consensus_config + .base_reward_per_uncle_percent, + ) + .unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.min_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.max_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_max_uncles_per_block).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_gas_limit).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.strategy).unwrap(), + //vm config + bcs_ext::to_bytes(&genesis_config.publishing_option.is_script_allowed()).unwrap(), + bcs_ext::to_bytes( + &genesis_config + .publishing_option + .is_module_publishing_allowed(), + ) + .unwrap(), + bcs_ext::to_bytes(&instruction_schedule).unwrap(), + bcs_ext::to_bytes(&native_schedule).unwrap(), + //gas constants + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .global_memory_per_byte_cost, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .global_memory_per_byte_write_cost, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .min_transaction_gas_units, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .large_transaction_cutoff, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .intrinsic_gas_per_byte, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .maximum_number_of_gas_units, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .min_price_per_gas_unit, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .max_price_per_gas_unit, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .max_transaction_size_in_bytes, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .gas_unit_scaling_factor, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .default_account_size, + ) + .unwrap(), + // dao config params + bcs_ext::to_bytes(&genesis_config.dao_config.voting_delay).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.voting_period).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.voting_quorum_rate).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.min_action_delay).unwrap(), + //transaction timeout config + bcs_ext::to_bytes(&genesis_config.transaction_timeout).unwrap(), + ]; + + if function == "initialize_v3" { + args.push( + bcs_ext::to_bytes(&GasSchedule::from(&genesis_config.vm_config.gas_schedule)).unwrap(), + ); + } + ScriptFunction::new( ModuleId::new(core_code_address(), Identifier::new("Genesis").unwrap()), - Identifier::new("initialize_v2").unwrap(), + Identifier::new(function).unwrap(), vec![], - vec![ - bcs_ext::to_bytes(&net.genesis_config().stdlib_version.version()).unwrap(), - bcs_ext::to_bytes(&genesis_config.reward_delay).unwrap(), - bcs_ext::to_bytes(&G_TOTAL_STC_AMOUNT.scaling()).unwrap(), - bcs_ext::to_bytes(&genesis_config.pre_mine_amount).unwrap(), - bcs_ext::to_bytes(&genesis_config.time_mint_amount).unwrap(), - bcs_ext::to_bytes(&genesis_config.time_mint_period).unwrap(), - bcs_ext::to_bytes(&genesis_parent_hash.to_vec()).unwrap(), - bcs_ext::to_bytes(&association_auth_key).unwrap(), - bcs_ext::to_bytes(&genesis_auth_key).unwrap(), - bcs_ext::to_bytes(&chain_id).unwrap(), - bcs_ext::to_bytes(&genesis_timestamp).unwrap(), - //consensus config - bcs_ext::to_bytes(&genesis_config.consensus_config.uncle_rate_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.epoch_block_count).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_difficulty_window) - .unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_reward_per_block).unwrap(), - bcs_ext::to_bytes( - &genesis_config - .consensus_config - .base_reward_per_uncle_percent, - ) - .unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.min_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.max_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_max_uncles_per_block).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_gas_limit).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.strategy).unwrap(), - //vm config - bcs_ext::to_bytes(&genesis_config.publishing_option.is_script_allowed()).unwrap(), - bcs_ext::to_bytes( - &genesis_config - .publishing_option - .is_module_publishing_allowed(), - ) - .unwrap(), - bcs_ext::to_bytes(&instruction_schedule).unwrap(), - bcs_ext::to_bytes(&native_schedule).unwrap(), - //gas constants - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .global_memory_per_byte_cost, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .global_memory_per_byte_write_cost, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .min_transaction_gas_units, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .large_transaction_cutoff, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .intrinsic_gas_per_byte, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .maximum_number_of_gas_units, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .min_price_per_gas_unit, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .max_price_per_gas_unit, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .max_transaction_size_in_bytes, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .gas_unit_scaling_factor, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .default_account_size, - ) - .unwrap(), - // dao config params - bcs_ext::to_bytes(&genesis_config.dao_config.voting_delay).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.voting_period).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.voting_quorum_rate).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.min_action_delay).unwrap(), - //transaction timeout config - bcs_ext::to_bytes(&genesis_config.transaction_timeout).unwrap(), - ], + args, ) } diff --git a/vm/types/Cargo.toml b/vm/types/Cargo.toml index 45a44f8412..ef7d5a8854 100644 --- a/vm/types/Cargo.toml +++ b/vm/types/Cargo.toml @@ -52,7 +52,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-vm-types" publish = { workspace = true } -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/types/src/account_config/constants/addresses.rs b/vm/types/src/account_config/constants/addresses.rs index 2d2a0bee6d..a2cff2c7c8 100644 --- a/vm/types/src/account_config/constants/addresses.rs +++ b/vm/types/src/account_config/constants/addresses.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::account_address::AccountAddress; + pub use move_core_types::language_storage::CORE_CODE_ADDRESS; -use once_cell::sync::Lazy; pub fn association_address() -> AccountAddress { AccountAddress::from_hex_literal("0xA550C18") @@ -17,35 +17,7 @@ pub fn genesis_address() -> AccountAddress { CORE_CODE_ADDRESS } -pub const TABLE_ADDRESS_LIST_LEN: usize = 32; -pub const TABLE_ADDRESS_LIST: [&str; TABLE_ADDRESS_LIST_LEN] = [ - "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", "0x38", "0x39", "0x3a", "0x3b", "0x3c", - "0x3d", "0x3e", "0x3f", "0x40", "0x41", "0x42", "0x43", "0x44", "0x45", "0x46", "0x47", "0x48", - "0x49", "0x4a", "0x4b", "0x4c", "0x4d", "0x4e", "0x4f", "0x50", -]; - -pub static TABLE_HANDLE_ADDRESS_LIST: Lazy> = Lazy::new(|| { - let mut arr = vec![]; - for str in TABLE_ADDRESS_LIST { - arr.push( - AccountAddress::from_hex_literal(str) - .expect("Parsing valid hex literal should always succeed"), - ); - } - arr -}); - -#[cfg(test)] -mod tests { - use crate::account_config::{TABLE_ADDRESS_LIST, TABLE_ADDRESS_LIST_LEN}; - use std::collections::HashSet; - - #[test] - fn test_table_handle_address_list_unique() { - let table_address_set: HashSet = TABLE_ADDRESS_LIST - .iter() - .map(|str| String::from(*str)) - .collect(); - assert_eq!(table_address_set.len(), TABLE_ADDRESS_LIST_LEN); - } +pub fn table_handle_address() -> AccountAddress { + AccountAddress::from_hex_literal("0xA550C68") + .expect("Parsing valid hex literal should always succeed") } diff --git a/vm/types/src/lib.rs b/vm/types/src/lib.rs index ea86f45141..6afe4cff0d 100644 --- a/vm/types/src/lib.rs +++ b/vm/types/src/lib.rs @@ -5,6 +5,7 @@ mod language_storage_ext; pub mod account_address; +pub mod dag_block_metadata; pub mod gas_schedule; pub mod location { pub use move_ir_types::location::Loc; diff --git a/vm/types/src/on_chain_config/gas_schedule.rs b/vm/types/src/on_chain_config/gas_schedule.rs index 669b9dbb56..8643e44010 100644 --- a/vm/types/src/on_chain_config/gas_schedule.rs +++ b/vm/types/src/on_chain_config/gas_schedule.rs @@ -15,6 +15,8 @@ use std::collections::BTreeMap; const GAS_SCHEDULE_MODULE_NAME: &str = "GasSchedule"; pub static G_GAS_SCHEDULE_IDENTIFIER: Lazy = Lazy::new(|| Identifier::new(GAS_SCHEDULE_MODULE_NAME).unwrap()); +pub static G_GAS_SCHEDULE_INITIALIZE: Lazy = + Lazy::new(|| Identifier::new("initialize").unwrap()); pub static G_GAS_SCHEDULE_GAS_SCHEDULE: Lazy = Lazy::new(|| Identifier::new("gas_schedule").unwrap()); diff --git a/vm/types/src/on_chain_config/mod.rs b/vm/types/src/on_chain_config/mod.rs index 774525f3b2..35c00f59e4 100644 --- a/vm/types/src/on_chain_config/mod.rs +++ b/vm/types/src/on_chain_config/mod.rs @@ -32,6 +32,7 @@ pub use self::{ native_gas_schedule_v2, native_gas_schedule_v3, native_gas_schedule_v4, txn_gas_schedule_test, txn_gas_schedule_v1, txn_gas_schedule_v2, txn_gas_schedule_v3, GasSchedule, G_GAS_SCHEDULE_GAS_SCHEDULE, G_GAS_SCHEDULE_IDENTIFIER, + G_GAS_SCHEDULE_INITIALIZE, }, genesis_gas_schedule::{ instruction_table_v1, instruction_table_v2, native_table_v1, native_table_v2, diff --git a/vm/types/src/proptest_types.rs b/vm/types/src/proptest_types.rs index 78bff6b101..6095a97054 100644 --- a/vm/types/src/proptest_types.rs +++ b/vm/types/src/proptest_types.rs @@ -318,7 +318,7 @@ impl Arbitrary for RawUserTransaction { } impl SignatureCheckedTransaction { - // This isn't an Arbitrary impl because this doesn't generate *any* possible SignedUserTransaction, + // This isn't an Arbitrary impl because this doesn't generate *any* possible SignedTransaction, // just one kind of them. pub fn script_strategy( keypair_strategy: impl Strategy>, diff --git a/vm/types/src/state_store/state_key.rs b/vm/types/src/state_store/state_key.rs index c452ad6feb..ef5ad5ac62 100644 --- a/vm/types/src/state_store/state_key.rs +++ b/vm/types/src/state_store/state_key.rs @@ -1,6 +1,9 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +// Copyright (c) Aptos +// SPDX-License-Identifier: Apache-2.0 + use crate::access_path::AccessPath; use crate::state_store::table::TableHandle; use schemars::{self, JsonSchema}; diff --git a/vm/types/src/state_store/table.rs b/vm/types/src/state_store/table.rs index fab209e847..ec1871586d 100644 --- a/vm/types/src/state_store/table.rs +++ b/vm/types/src/state_store/table.rs @@ -1,8 +1,9 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::account_config::TABLE_ADDRESS_LIST_LEN; -use anyhow::format_err; +// Copyright (c) Aptos +// SPDX-License-Identifier: Apache-2.0 + use move_core_types::{ account_address::{AccountAddress, AccountAddressParseError}, language_storage::TypeTag, @@ -21,15 +22,6 @@ impl TableHandle { pub fn size(&self) -> usize { std::mem::size_of_val(&self.0) } - - pub fn get_idx(&self) -> Result { - let binding = self.0.into_bytes(); - let val = binding.last(); - match val { - Some(val) => Ok(*val as usize & (TABLE_ADDRESS_LIST_LEN - 1)), - _ => Err(format_err!("TableHandle array size > 0")), - } - } } impl FromStr for TableHandle { @@ -61,25 +53,3 @@ impl TableInfo { } } } - -#[cfg(test)] -mod tests { - use crate::state_store::table::TableHandle; - use move_core_types::account_address::AccountAddress; - - #[test] - fn test_get_idx() -> Result<(), anyhow::Error> { - let hdl1 = TableHandle(AccountAddress::ZERO); - let idx1 = hdl1.get_idx()?; - assert_eq!(idx1, 0); - - let hdl2 = TableHandle(AccountAddress::ONE); - let idx2 = hdl2.get_idx()?; - assert_eq!(idx2, 1); - - let hdl3 = TableHandle(AccountAddress::TWO); - let idx3 = hdl3.get_idx()?; - assert_eq!(idx3, 2); - Ok(()) - } -} diff --git a/vm/types/src/transaction/mod.rs b/vm/types/src/transaction/mod.rs index 5a083a80ec..34de8b6ec4 100644 --- a/vm/types/src/transaction/mod.rs +++ b/vm/types/src/transaction/mod.rs @@ -26,12 +26,10 @@ use starcoin_crypto::{ traits::*, HashValue, }; -use std::collections::BTreeMap; use std::ops::Deref; use std::{convert::TryFrom, fmt}; use crate::state_store::state_key::StateKey; -use crate::state_store::table::{TableHandle, TableInfo}; use crate::write_set::WriteOp; pub use error::CallError; pub use error::Error as TransactionError; @@ -473,7 +471,7 @@ impl fmt::Debug for SignedUserTransaction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "SignedUserTransaction {{ \n \ + "SignedTransaction {{ \n \ {{ raw_txn: {:#?}, \n \ authenticator: {:#?}, \n \ }} \n \ @@ -618,9 +616,6 @@ pub enum TransactionStatus { /// Keep the transaction output Keep(KeptVMStatus), - - /// Retry the transaction, e.g., after a reconfiguration - Retry, } impl TransactionStatus { @@ -628,7 +623,6 @@ impl TransactionStatus { match self { TransactionStatus::Keep(status) => Ok(status.clone()), TransactionStatus::Discard(code) => Err(*code), - TransactionStatus::Retry => Err(StatusCode::UNKNOWN_VALIDATION_STATUS), } } @@ -636,7 +630,6 @@ impl TransactionStatus { match self { TransactionStatus::Discard(_) => true, TransactionStatus::Keep(_) => false, - TransactionStatus::Retry => true, } } } @@ -653,8 +646,6 @@ impl From for TransactionStatus { /// The output of executing a transaction. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct TransactionOutput { - table_infos: BTreeMap, - write_set: WriteSet, /// The list of events emitted during this transaction. @@ -669,14 +660,12 @@ pub struct TransactionOutput { impl TransactionOutput { pub fn new( - table_infos: BTreeMap, write_set: WriteSet, events: Vec, gas_used: u64, status: TransactionStatus, ) -> Self { TransactionOutput { - table_infos, write_set, events, gas_used, @@ -688,10 +677,6 @@ impl TransactionOutput { &self.write_set } - pub fn table_infos(&self) -> &BTreeMap { - &self.table_infos - } - pub fn events(&self) -> &[ContractEvent] { &self.events } @@ -704,22 +689,8 @@ impl TransactionOutput { &self.status } - pub fn into_inner( - self, - ) -> ( - BTreeMap, - WriteSet, - Vec, - u64, - TransactionStatus, - ) { - ( - self.table_infos, - self.write_set, - self.events, - self.gas_used, - self.status, - ) + pub fn into_inner(self) -> (WriteSet, Vec, u64, TransactionStatus) { + (self.write_set, self.events, self.gas_used, self.status) } pub fn table_items(&self) -> Vec<(StateKey, WriteOp)> { diff --git a/vm/vm-runtime/Cargo.toml b/vm/vm-runtime/Cargo.toml index 4cdb2832df..1a720424c0 100644 --- a/vm/vm-runtime/Cargo.toml +++ b/vm/vm-runtime/Cargo.toml @@ -1,14 +1,3 @@ -[package] -authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } -name = "starcoin-vm-runtime" -publish = { workspace = true } -version = "1.13.7" -homepage = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - [dependencies] anyhow = { workspace = true } bcs-ext = { package = "bcs-ext", workspace = true } @@ -31,9 +20,6 @@ starcoin-metrics = { optional = true, workspace = true } starcoin-gas = { workspace = true } starcoin-gas-algebra-ext = { workspace = true } serde = { features = ["derive"], workspace = true } -starcoin-parallel-executor = { workspace = true } -rayon = { workspace = true } -num_cpus = { workspace = true } [dev-dependencies] stdlib = { package = "stdlib", workspace = true } @@ -42,3 +28,14 @@ stdlib = { package = "stdlib", workspace = true } default = ["metrics"] metrics = ["starcoin-metrics"] testing = ["move-stdlib/testing", "starcoin-natives/testing"] + +[package] +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +name = "starcoin-vm-runtime" +publish = { workspace = true } +version = "1.13.5" +homepage = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } diff --git a/vm/vm-runtime/src/data_cache.rs b/vm/vm-runtime/src/data_cache.rs index f380f71410..b38de555bb 100644 --- a/vm/vm-runtime/src/data_cache.rs +++ b/vm/vm-runtime/src/data_cache.rs @@ -135,7 +135,6 @@ impl<'a, S: StateView> ResourceResolver for RemoteStorage<'a, S> { self.get(&ap).map_err(|e| e.finish(Location::Undefined)) } } - // TODO Note for Conflicting: conflicting implementation in crate `starcoin_vm_types`: - impl ConfigStorage for V where V: StateView; // impl<'a, S: StateView> ConfigStorage for RemoteStorage<'a, S> { // fn fetch_config(&self, access_path: AccessPath) -> Option> { diff --git a/vm/vm-runtime/src/lib.rs b/vm/vm-runtime/src/lib.rs index 2680fefa62..7b3bcc534d 100644 --- a/vm/vm-runtime/src/lib.rs +++ b/vm/vm-runtime/src/lib.rs @@ -1,43 +1,19 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -mod adapter_common; pub mod data_cache; #[cfg(feature = "metrics")] pub mod metrics; pub mod natives; pub mod starcoin_vm; - -use move_core_types::vm_status::VMStatus; -pub use move_vm_runtime::{move_vm, session}; +pub use move_vm_runtime::move_vm; +pub use move_vm_runtime::session; mod access_path_cache; mod errors; pub mod move_vm_ext; -pub mod parallel_executor; -use crate::metrics::VMMetrics; -use starcoin_vm_types::{ - access_path::AccessPath, - account_address::AccountAddress, - language_storage::StructTag, - state_view::StateView, - transaction::{Transaction, TransactionOutput}, -}; - -/// This trait describes the VM's execution interface. -pub trait VMExecutor: Send + Sync { - // NOTE: At the moment there are no persistent caches that live past the end of a block (that's - // why execute_block doesn't take &self.) - // There are some cache invalidation issues around transactions publishing code that need to be - // sorted out before that's possible. - - /// Executes a block of transactions and returns output for each one of them. - fn execute_block( - transactions: Vec, - state_view: &impl StateView, - block_gas_limit: Option, - metrics: Option, - ) -> Result, VMStatus>; -} +use starcoin_vm_types::access_path::AccessPath; +use starcoin_vm_types::account_address::AccountAddress; +use starcoin_vm_types::language_storage::StructTag; /// Get the AccessPath to a resource stored under `address` with type name `tag` fn create_access_path(address: AccountAddress, tag: StructTag) -> AccessPath { diff --git a/vm/vm-runtime/src/move_vm_ext/session.rs b/vm/vm-runtime/src/move_vm_ext/session.rs index 57c3b8aa42..c427d4c188 100644 --- a/vm/vm-runtime/src/move_vm_ext/session.rs +++ b/vm/vm-runtime/src/move_vm_ext/session.rs @@ -13,11 +13,9 @@ use starcoin_vm_types::block_metadata::BlockMetadata; use starcoin_vm_types::contract_event::ContractEvent; use starcoin_vm_types::event::EventKey; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use starcoin_vm_types::transaction::SignatureCheckedTransaction; use starcoin_vm_types::transaction_metadata::TransactionMetadata; use starcoin_vm_types::write_set::{WriteOp, WriteSet, WriteSetMut}; -use std::collections::BTreeMap; use std::convert::TryFrom; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash)] @@ -79,14 +77,7 @@ impl SessionOutput { pub fn into_change_set( self, ap_cache: &mut C, - ) -> Result< - ( - BTreeMap, - WriteSet, - Vec, - ), - VMStatus, - > { + ) -> Result<(WriteSet, Vec), VMStatus> { let Self { change_set, events, @@ -136,14 +127,6 @@ impl SessionOutput { } } - let mut table_infos = BTreeMap::new(); - for (key, value) in table_change_set.new_tables { - let handle = TableHandle(key.0); - let info = TableInfo::new(value.key_type, value.value_type); - - table_infos.insert(handle, info); - } - let write_set = write_set_mut .freeze() .map_err(|_| VMStatus::Error(StatusCode::DATA_FORMAT_ERROR))?; @@ -157,6 +140,6 @@ impl SessionOutput { }) .collect::, VMStatus>>()?; - Ok((table_infos, write_set, events)) + Ok((write_set, events)) } } diff --git a/vm/vm-runtime/src/starcoin_vm.rs b/vm/vm-runtime/src/starcoin_vm.rs index c9402e5f51..48a3088938 100644 --- a/vm/vm-runtime/src/starcoin_vm.rs +++ b/vm/vm-runtime/src/starcoin_vm.rs @@ -2,9 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::access_path_cache::AccessPathCache; -use crate::adapter_common::{ - discard_error_output, discard_error_vm_status, PreprocessedTransaction, VMAdapter, -}; use crate::data_cache::{AsMoveResolver, RemoteStorage, StateViewCache}; use crate::errors::{ convert_normal_success_epilogue_error, convert_prologue_runtime_error, error_split, @@ -15,8 +12,6 @@ use move_core_types::gas_algebra::{InternalGasPerByte, NumBytes}; use move_table_extension::NativeTableContext; use move_vm_runtime::move_vm_adapter::{PublishModuleBundleOption, SessionAdapter}; use move_vm_runtime::session::Session; -use num_cpus; -use once_cell::sync::OnceCell; use starcoin_config::genesis_config::G_LATEST_GAS_PARAMS; use starcoin_crypto::HashValue; use starcoin_gas::{NativeGasParameters, StarcoinGasMeter, StarcoinGasParameters}; @@ -35,6 +30,7 @@ use starcoin_types::{ SignatureCheckedTransaction, SignedUserTransaction, Transaction, TransactionOutput, TransactionPayload, TransactionStatus, }, + write_set::WriteSet, }; use starcoin_vm_types::access::{ModuleAccess, ScriptAccess}; use starcoin_vm_types::account_address::AccountAddress; @@ -43,15 +39,15 @@ use starcoin_vm_types::account_config::{ core_code_address, genesis_address, ModuleUpgradeStrategy, TwoPhaseUpgradeV2Resource, G_EPILOGUE_NAME, G_EPILOGUE_V2_NAME, G_PROLOGUE_NAME, }; -use starcoin_vm_types::errors::VMResult; use starcoin_vm_types::file_format::{CompiledModule, CompiledScript}; use starcoin_vm_types::gas_schedule::G_LATEST_GAS_COST_TABLE; use starcoin_vm_types::genesis_config::StdlibVersion; use starcoin_vm_types::identifier::IdentStr; use starcoin_vm_types::language_storage::ModuleId; use starcoin_vm_types::on_chain_config::{ - GasSchedule, MoveLanguageVersion, G_GAS_CONSTANTS_IDENTIFIER, - G_INSTRUCTION_SCHEDULE_IDENTIFIER, G_NATIVE_SCHEDULE_IDENTIFIER, G_VM_CONFIG_IDENTIFIER, + GasSchedule, MoveLanguageVersion, G_GAS_CONSTANTS_IDENTIFIER, G_GAS_SCHEDULE_GAS_SCHEDULE, + G_GAS_SCHEDULE_IDENTIFIER, G_INSTRUCTION_SCHEDULE_IDENTIFIER, G_NATIVE_SCHEDULE_IDENTIFIER, + G_VM_CONFIG_IDENTIFIER, }; use starcoin_vm_types::state_store::state_key::StateKey; use starcoin_vm_types::state_view::StateReaderExt; @@ -67,14 +63,10 @@ use starcoin_vm_types::{ transaction_metadata::TransactionMetadata, vm_status::{StatusCode, VMStatus}, }; -use std::cmp::min; use std::sync::Arc; -static EXECUTION_CONCURRENCY_LEVEL: OnceCell = OnceCell::new(); - #[cfg(feature = "metrics")] use crate::metrics::VMMetrics; -use crate::VMExecutor; #[derive(Clone)] #[allow(clippy::upper_case_acronyms)] @@ -187,7 +179,7 @@ impl StarcoinVM { < StdlibVersion::Version(VMCONFIG_UPGRADE_VERSION_MARK) { debug!( - "stdlib version: {}, fetch VMConfig from onchain resource", + "stdlib version: {}, fetch vmconfig from onchain resource", stdlib_version ); let gas_cost_table = VMConfig::fetch_config(&remote_storage)? @@ -195,13 +187,13 @@ impl StarcoinVM { .gas_schedule; ( Some(GasSchedule::from(&gas_cost_table)), - "gas schedule from VMConfig", + "gas schedule from gensis", ) } else if stdlib_version >= StdlibVersion::Version(VMCONFIG_UPGRADE_VERSION_MARK) && stdlib_version < StdlibVersion::Version(GAS_SCHEDULE_UPGRADE_VERSION_MARK) { debug!( - "stdlib version: {}, fetch VMConfig from onchain module", + "stdlib version: {}, fetch vmconfig from onchain module", stdlib_version ); let instruction_schedule = { @@ -261,15 +253,33 @@ impl StarcoinVM { }; ( Some(GasSchedule::from(&cost_table)), - "gas schedule from VMConfig", + "gas schedule from vm config", ) } else { debug!( - "stdlib version: {}, fetch schedule from onchain module GasSchedule", + "stdlib version: {}, fetch gas schedule from onchain module", stdlib_version ); - let gas_schedule = GasSchedule::fetch_config(&remote_storage)?; - (gas_schedule, "gas schedule from GasSchedule") + let gas_schedule = { + let data = self + .execute_readonly_function_internal( + state, + &ModuleId::new( + core_code_address(), + G_GAS_SCHEDULE_IDENTIFIER.to_owned(), + ), + G_GAS_SCHEDULE_GAS_SCHEDULE.as_ident_str(), + vec![], + vec![], + false, + )? + .pop() + .ok_or_else(|| { + anyhow::anyhow!("Expect 0x1::GasSchedule::initialize() return value") + })?; + bcs_ext::from_bytes::(&data)? + }; + (Some(gas_schedule), "gas schedule from gas schedule in move") }; #[cfg(feature = "print_gas_info")] match self.gas_schedule.as_ref() { @@ -914,6 +924,7 @@ impl StarcoinVM { storage: &S, txn: SignedUserTransaction, ) -> (VMStatus, TransactionOutput) { + let txn_id = txn.id(); let txn_data = match TransactionMetadata::new(&txn) { Ok(txn_data) => txn_data, Err(e) => { @@ -961,7 +972,7 @@ impl StarcoinVM { match result { Ok(status_and_output) => { log_vm_status( - txn.id(), + txn_id, &txn_data, &status_and_output.0, Some(&status_and_output.1), @@ -970,7 +981,7 @@ impl StarcoinVM { } Err(err) => { let txn_status = TransactionStatus::from(err.clone()); - log_vm_status(txn.id(), &txn_data, &err, None); + log_vm_status(txn_id, &txn_data, &err, None); if txn_status.is_discarded() { discard_error_vm_status(err) } else { @@ -1052,6 +1063,8 @@ impl StarcoinVM { Ok(()) } + /// XXX FIXME YSG add delta_change_set for TransactionOutput + /// Execute a block transactions with gas_limit, /// if gas is used up when executing some txn, only return the outputs of previous succeed txns. pub fn execute_block_transactions( @@ -1059,13 +1072,12 @@ impl StarcoinVM { storage: &S, transactions: Vec, block_gas_limit: Option, - ) -> Result, VMStatus> { + ) -> Result> { let mut data_cache = StateViewCache::new(storage); let mut result = vec![]; // TODO load config by config change event - self.load_configs(&data_cache) - .map_err(|_err| VMStatus::Error(StatusCode::STORAGE_ERROR))?; + self.load_configs(&data_cache)?; let mut gas_left = block_gas_limit.unwrap_or(u64::MAX); @@ -1104,8 +1116,7 @@ impl StarcoinVM { data_cache.push_write_set(output.write_set()) } // TODO load config by config change event - self.check_reconfigure(&data_cache, &output) - .map_err(|_err| VMStatus::Error(StatusCode::STORAGE_ERROR))?; + self.check_reconfigure(&data_cache, &output)?; #[cfg(feature = "metrics")] if let Some(timer) = timer { @@ -1254,9 +1265,7 @@ impl StarcoinVM { let table_change_set = table_context .into_change_set() .map_err(|e| e.finish(Location::Undefined))?; - // Ignore new table infos. - // No table infos should be produced in readonly function. - let (_table_infos, write_set, _events) = SessionOutput { + let (write_set, _events) = SessionOutput { change_set, events, table_change_set, @@ -1326,7 +1335,6 @@ impl StarcoinVM { TransactionStatus::Discard(status) => { (VMStatus::Error(status), discard_error_output(status)) } - TransactionStatus::Retry => unreachable!(), } } @@ -1336,43 +1344,6 @@ impl StarcoinVM { VMStatus::Error(StatusCode::VM_STARTUP_FAILURE) }) } - - /// Sets execution concurrency level when invoked the first time. - pub fn set_concurrency_level_once(mut concurrency_level: usize) { - concurrency_level = min(concurrency_level, num_cpus::get()); - // Only the first call succeeds, due to OnceCell semantics. - EXECUTION_CONCURRENCY_LEVEL.set(concurrency_level).ok(); - info!("TurboSTM executor concurrency_level {}", concurrency_level); - } - - /// Get the concurrency level if already set, otherwise return default 1 - /// (sequential execution). - pub fn get_concurrency_level() -> usize { - match EXECUTION_CONCURRENCY_LEVEL.get() { - Some(concurrency_level) => *concurrency_level, - None => 1, - } - } - - /// Alternate form of 'execute_block' that keeps the vm_status before it goes into the - /// `TransactionOutput` - pub fn execute_block_and_keep_vm_status( - txns: Vec, - state_view: &impl StateView, - block_gas_limit: Option, - metrics: Option, - ) -> Result, VMStatus> { - let mut vm = StarcoinVM::new(metrics); - vm.execute_block_transactions(state_view, txns, block_gas_limit) - } - - pub fn load_module<'r, R: MoveResolverExt>( - &self, - module_id: &ModuleId, - remote: &'r R, - ) -> VMResult> { - self.move_vm.load_module(module_id, remote) - } } #[allow(clippy::large_enum_variant)] @@ -1390,7 +1361,6 @@ impl TransactionBlock { } } -/// TransactionBlock::UserTransaction | TransactionBlock::BlockPrologue | TransactionBlock::UserTransaction pub fn chunk_block_transactions(txns: Vec) -> Vec { let mut blocks = vec![]; let mut buf = vec![]; @@ -1433,6 +1403,30 @@ pub(crate) fn charge_global_write_gas_usage( .map_err(|p_err| p_err.finish(Location::Undefined).into_vm_status()) } +pub(crate) fn discard_error_vm_status(err: VMStatus) -> (VMStatus, TransactionOutput) { + info!("discard error vm_status output: {:?}", err); + let vm_status = err.clone(); + let error_code = match err.keep_or_discard() { + Ok(_) => { + debug_assert!(false, "discarding non-discardable error: {:?}", vm_status); + vm_status.status_code() + } + Err(code) => code, + }; + (vm_status, discard_error_output(error_code)) +} + +pub(crate) fn discard_error_output(err: StatusCode) -> TransactionOutput { + info!("discard error output: {:?}", err); + // Since this transaction will be discarded, no writeset will be included. + TransactionOutput::new( + WriteSet::default(), + vec![], + 0, + TransactionStatus::Discard(err), + ) +} + pub(crate) fn get_transaction_output( ap_cache: &mut A, session: SessionAdapter, @@ -1450,14 +1444,13 @@ pub(crate) fn get_transaction_output( let table_change_set = table_context .into_change_set() .map_err(|e| e.finish(Location::Undefined))?; - let (table_infos, write_set, events) = SessionOutput { + let (write_set, events) = SessionOutput { change_set, events, table_change_set, } .into_change_set(ap_cache)?; Ok(TransactionOutput::new( - table_infos, write_set, events, u64::from(gas_used), @@ -1500,93 +1493,3 @@ pub fn log_vm_status( } } } - -// Executor external API -impl VMExecutor for StarcoinVM { - /// Execute a block of `transactions`. The output vector will have the exact same length as the - /// input vector. The discarded transactions will be marked as `TransactionStatus::Discard` and - /// have an empty `WriteSet`. Also `state_view` is immutable, and does not have interior - /// mutability. Writes to be applied to the data view are encoded in the write set part of a - /// transaction output. - fn execute_block( - transactions: Vec, - state_view: &impl StateView, - block_gas_limit: Option, - metrics: Option, - ) -> Result, VMStatus> { - let concurrency_level = Self::get_concurrency_level(); - if concurrency_level > 1 { - let (result, _) = crate::parallel_executor::ParallelStarcoinVM::execute_block( - transactions, - state_view, - concurrency_level, - block_gas_limit, - metrics, - )?; - // debug!("TurboSTM executor concurrency_level {}", concurrency_level); - Ok(result) - } else { - let output = Self::execute_block_and_keep_vm_status( - transactions, - state_view, - block_gas_limit, - metrics, - )?; - Ok(output - .into_iter() - .map(|(_vm_status, txn_output)| txn_output) - .collect()) - } - } -} - -impl VMAdapter for StarcoinVM { - fn new_session<'r, R: MoveResolverExt>( - &self, - remote: &'r R, - session_id: SessionId, - ) -> SessionAdapter<'r, '_, R> { - self.move_vm.new_session(remote, session_id).into() - } - - fn check_signature(txn: SignedUserTransaction) -> Result { - txn.check_signature() - } - - fn should_restart_execution(output: &TransactionOutput) -> bool { - // XXX FIXME YSG if GasSchedule.move UpgradeEvent - for event in output.events() { - if event.key().get_creator_address() == genesis_address() - && (event.is::() || event.is::>()) - { - info!("should_restart_execution happen"); - return true; - } - } - false - } - - fn execute_single_transaction( - &self, - txn: &PreprocessedTransaction, - data_cache: &S, - ) -> Result<(VMStatus, TransactionOutput, Option), VMStatus> { - Ok(match txn { - PreprocessedTransaction::UserTransaction(txn) => { - let sender = txn.sender().to_string(); - let (vm_status, output) = self.execute_user_transaction(data_cache, *txn.clone()); - // XXX FIXME YSG - // let gas_unit_price = transaction.gas_unit_price(); think about gas_used OutOfGas - (vm_status, output, Some(sender)) - } - PreprocessedTransaction::BlockMetadata(block_meta) => { - let (vm_status, output) = - match self.process_block_metadata(data_cache, block_meta.clone()) { - Ok(output) => (VMStatus::Executed, output), - Err(vm_status) => discard_error_vm_status(vm_status), - }; - (vm_status, output, Some("block_meta".to_string())) - } - }) - } -} diff --git a/vm/vm-status-translator/Cargo.toml b/vm/vm-status-translator/Cargo.toml index ac17b4b4fe..b97f078fe6 100644 --- a/vm/vm-status-translator/Cargo.toml +++ b/vm/vm-status-translator/Cargo.toml @@ -9,7 +9,7 @@ starcoin-vm-types = { workspace = true } authors = { workspace = true } edition = { workspace = true } name = "vm-status-translator" -version = "1.13.7" +version = "1.13.5" homepage = { workspace = true } license = { workspace = true } publish = { workspace = true } From 53fc77bc7661757dd4711f3a521418e697a863ad Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Mon, 16 Oct 2023 11:24:49 +0800 Subject: [PATCH 07/17] merge master and dag --- consensus/src/consensusdb/access.rs | 199 +++++ consensus/src/consensusdb/cache.rs | 44 ++ .../src/consensusdb/consensus_ghostdag.rs | 512 +++++++++++++ consensus/src/consensusdb/consensus_header.rs | 216 ++++++ .../src/consensusdb/consensus_reachability.rs | 540 ++++++++++++++ .../src/consensusdb/consensus_relations.rs | 316 ++++++++ consensus/src/consensusdb/db.rs | 149 ++++ consensus/src/consensusdb/error.rs | 58 ++ consensus/src/consensusdb/item.rs | 81 +++ consensus/src/consensusdb/mod.rs | 31 + consensus/src/consensusdb/schema.rs | 40 + consensus/src/consensusdb/writer.rs | 75 ++ consensus/src/dag/blockdag.rs | 260 +++++++ consensus/src/dag/ghostdag/mergeset.rs | 71 ++ consensus/src/dag/ghostdag/mod.rs | 4 + consensus/src/dag/ghostdag/protocol.rs | 338 +++++++++ consensus/src/dag/ghostdag/util.rs | 57 ++ consensus/src/dag/mod.rs | 4 + consensus/src/dag/reachability/extensions.rs | 50 ++ consensus/src/dag/reachability/inquirer.rs | 345 +++++++++ consensus/src/dag/reachability/mod.rs | 50 ++ .../dag/reachability/reachability_service.rs | 315 ++++++++ consensus/src/dag/reachability/reindex.rs | 684 ++++++++++++++++++ .../src/dag/reachability/relations_service.rs | 34 + consensus/src/dag/reachability/tests.rs | 264 +++++++ consensus/src/dag/reachability/tree.rs | 161 +++++ consensus/src/dag/types/ghostdata.rs | 147 ++++ consensus/src/dag/types/interval.rs | 377 ++++++++++ consensus/src/dag/types/mod.rs | 6 + consensus/src/dag/types/ordering.rs | 36 + consensus/src/dag/types/perf.rs | 51 ++ consensus/src/dag/types/reachability.rs | 26 + consensus/src/dag/types/trusted.rs | 26 + executor/benchmark/src/lib.rs | 2 +- network-rpc/api/src/dag_protocol.rs | 49 ++ node/src/node.rs | 7 +- storage/src/flexi_dag/mod.rs | 76 ++ storage/src/tests/test_dag.rs | 347 +++++++++ .../test_write_dag_block_chain.rs | 215 ++++++ sync/src/tasks/block_sync_task.rs | 54 ++ sync/src/tasks/sync_dag_accumulator_task.rs | 169 +++++ sync/src/tasks/sync_dag_block_task.rs | 174 +++++ sync/src/tasks/sync_dag_full_task.rs | 338 +++++++++ sync/src/tasks/sync_dag_protocol_trait.rs | 29 + sync/src/tasks/sync_find_ancestor_task.rs | 115 +++ types/src/blockhash.rs | 71 ++ types/src/header.rs | 60 ++ .../compiled/12/11-12/stdlib/052_Epoch.mv | Bin 0 -> 2724 bytes .../compiled/12/11-12/stdlib/053_EventUtil.mv | Bin 0 -> 490 bytes .../12/11-12/stdlib/054_FixedPoint32.mv | Bin 0 -> 595 bytes .../12/11-12/stdlib/055_GasSchedule.mv | Bin 0 -> 8542 bytes .../stdlib/056_GenesisSignerCapability.mv | Bin 0 -> 454 bytes .../compiled/12/11-12/stdlib/057_Oracle.mv | Bin 0 -> 1982 bytes .../12/11-12/stdlib/058_PriceOracle.mv | Bin 0 -> 825 bytes .../12/11-12/stdlib/059_STCUSDOracle.mv | Bin 0 -> 322 bytes .../compiled/12/11-12/stdlib/060_Offer.mv | Bin 0 -> 538 bytes vm/stdlib/compiled/12/11-12/stdlib/061_NFT.mv | Bin 0 -> 4041 bytes .../12/11-12/stdlib/062_LanguageVersion.mv | Bin 0 -> 143 bytes .../12/11-12/stdlib/063_MerkleProof.mv | Bin 0 -> 369 bytes .../11-12/stdlib/064_MerkleNFTDistributor.mv | Bin 0 -> 1338 bytes .../12/11-12/stdlib/065_IdentifierNFT.mv | Bin 0 -> 1493 bytes .../12/11-12/stdlib/066_GenesisNFT.mv | Bin 0 -> 1242 bytes .../11-12/stdlib/067_StdlibUpgradeScripts.mv | Bin 0 -> 1943 bytes .../compiled/12/11-12/stdlib/068_Genesis.mv | Bin 0 -> 3629 bytes .../12/11-12/stdlib/069_GenesisNFTScripts.mv | Bin 0 -> 125 bytes .../11-12/stdlib/070_IdentifierNFTScripts.mv | Bin 0 -> 204 bytes .../12/11-12/stdlib/071_MintDaoProposal.mv | Bin 0 -> 681 bytes .../11-12/stdlib/072_ModuleUpgradeScripts.mv | Bin 0 -> 901 bytes .../12/11-12/stdlib/073_NFTGallery.mv | Bin 0 -> 2260 bytes .../12/11-12/stdlib/074_NFTGalleryScripts.mv | Bin 0 -> 271 bytes .../11-12/stdlib/075_OnChainConfigScripts.mv | Bin 0 -> 1130 bytes .../11-12/stdlib/076_PriceOracleAggregator.mv | Bin 0 -> 570 bytes .../12/11-12/stdlib/077_PriceOracleScripts.mv | Bin 0 -> 274 bytes .../compiled/12/11-12/stdlib/078_Secp256k1.mv | Bin 0 -> 633 bytes .../compiled/12/11-12/stdlib/079_Signature.mv | Bin 0 -> 430 bytes .../stdlib/080_SharedEd25519PublicKey.mv | Bin 0 -> 615 bytes .../compiled/12/11-12/stdlib/081_SimpleMap.mv | Bin 0 -> 1281 bytes .../12/11-12/stdlib/082_StructuredHash.mv | Bin 0 -> 270 bytes .../12/11-12/stdlib/083_StarcoinVerifier.mv | Bin 0 -> 1988 bytes .../compiled/12/11-12/stdlib/084_String.mv | Bin 0 -> 936 bytes .../compiled/12/11-12/stdlib/085_Table.mv | Bin 0 -> 1107 bytes .../12/11-12/stdlib/086_TransactionTimeout.mv | Bin 0 -> 293 bytes .../12/11-12/stdlib/087_TransactionManager.mv | Bin 0 -> 1307 bytes .../12/11-12/stdlib/088_TransferScripts.mv | Bin 0 -> 777 bytes .../12/11-12/stdlib/089_TreasuryScripts.mv | Bin 0 -> 892 bytes .../compiled/12/11-12/stdlib/090_TypeInfo.mv | Bin 0 -> 341 bytes .../compiled/12/11-12/stdlib/091_U256.mv | Bin 0 -> 1196 bytes .../12/11-12/stdlib/092_YieldFarming.mv | Bin 0 -> 1610 bytes .../12/11-12/stdlib/093_YieldFarmingV2.mv | Bin 0 -> 3429 bytes vm/stdlib/compiled/12/stdlib/052_Epoch.mv | Bin 0 -> 2724 bytes vm/stdlib/compiled/12/stdlib/053_EventUtil.mv | Bin 0 -> 490 bytes .../compiled/12/stdlib/054_FixedPoint32.mv | Bin 0 -> 595 bytes .../compiled/12/stdlib/055_GasSchedule.mv | Bin 0 -> 8542 bytes .../12/stdlib/056_GenesisSignerCapability.mv | Bin 0 -> 454 bytes vm/stdlib/compiled/12/stdlib/057_Oracle.mv | Bin 0 -> 1982 bytes .../compiled/12/stdlib/058_PriceOracle.mv | Bin 0 -> 825 bytes .../compiled/12/stdlib/059_STCUSDOracle.mv | Bin 0 -> 322 bytes vm/stdlib/compiled/12/stdlib/060_Offer.mv | Bin 0 -> 538 bytes vm/stdlib/compiled/12/stdlib/061_NFT.mv | Bin 0 -> 4041 bytes .../compiled/12/stdlib/062_LanguageVersion.mv | Bin 0 -> 143 bytes .../compiled/12/stdlib/063_MerkleProof.mv | Bin 0 -> 369 bytes .../12/stdlib/064_MerkleNFTDistributor.mv | Bin 0 -> 1338 bytes .../compiled/12/stdlib/065_IdentifierNFT.mv | Bin 0 -> 1493 bytes .../compiled/12/stdlib/066_GenesisNFT.mv | Bin 0 -> 1242 bytes .../12/stdlib/067_StdlibUpgradeScripts.mv | Bin 0 -> 1943 bytes vm/stdlib/compiled/12/stdlib/068_Genesis.mv | Bin 0 -> 3629 bytes .../12/stdlib/069_GenesisNFTScripts.mv | Bin 0 -> 125 bytes .../12/stdlib/070_IdentifierNFTScripts.mv | Bin 0 -> 204 bytes .../compiled/12/stdlib/071_MintDaoProposal.mv | Bin 0 -> 681 bytes .../12/stdlib/072_ModuleUpgradeScripts.mv | Bin 0 -> 901 bytes .../compiled/12/stdlib/073_NFTGallery.mv | Bin 0 -> 2260 bytes .../12/stdlib/074_NFTGalleryScripts.mv | Bin 0 -> 271 bytes .../12/stdlib/075_OnChainConfigScripts.mv | Bin 0 -> 1130 bytes .../12/stdlib/076_PriceOracleAggregator.mv | Bin 0 -> 570 bytes .../12/stdlib/077_PriceOracleScripts.mv | Bin 0 -> 274 bytes vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv | Bin 0 -> 633 bytes vm/stdlib/compiled/12/stdlib/079_Signature.mv | Bin 0 -> 430 bytes .../12/stdlib/080_SharedEd25519PublicKey.mv | Bin 0 -> 615 bytes vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv | Bin 0 -> 1281 bytes .../compiled/12/stdlib/082_StructuredHash.mv | Bin 0 -> 270 bytes .../12/stdlib/083_StarcoinVerifier.mv | Bin 0 -> 1988 bytes vm/stdlib/compiled/12/stdlib/084_String.mv | Bin 0 -> 936 bytes vm/stdlib/compiled/12/stdlib/085_Table.mv | Bin 0 -> 1107 bytes .../12/stdlib/086_TransactionTimeout.mv | Bin 0 -> 293 bytes .../12/stdlib/087_TransactionManager.mv | Bin 0 -> 1307 bytes .../compiled/12/stdlib/088_TransferScripts.mv | Bin 0 -> 777 bytes .../compiled/12/stdlib/089_TreasuryScripts.mv | Bin 0 -> 892 bytes vm/stdlib/compiled/12/stdlib/090_TypeInfo.mv | Bin 0 -> 341 bytes vm/stdlib/compiled/12/stdlib/091_U256.mv | Bin 0 -> 1196 bytes .../compiled/12/stdlib/092_YieldFarming.mv | Bin 0 -> 1610 bytes .../compiled/12/stdlib/093_YieldFarmingV2.mv | Bin 0 -> 3429 bytes vm/stdlib/compiled/latest/stdlib/052_Epoch.mv | Bin 0 -> 2724 bytes .../compiled/latest/stdlib/053_EventUtil.mv | Bin 0 -> 490 bytes .../latest/stdlib/054_FixedPoint32.mv | Bin 0 -> 595 bytes .../compiled/latest/stdlib/055_GasSchedule.mv | Bin 0 -> 8542 bytes .../stdlib/056_GenesisSignerCapability.mv | Bin 0 -> 454 bytes .../compiled/latest/stdlib/057_Oracle.mv | Bin 0 -> 1982 bytes .../compiled/latest/stdlib/058_PriceOracle.mv | Bin 0 -> 825 bytes .../latest/stdlib/059_STCUSDOracle.mv | Bin 0 -> 322 bytes vm/stdlib/compiled/latest/stdlib/060_Offer.mv | Bin 0 -> 538 bytes vm/stdlib/compiled/latest/stdlib/061_NFT.mv | Bin 0 -> 4041 bytes .../latest/stdlib/062_LanguageVersion.mv | Bin 0 -> 143 bytes .../compiled/latest/stdlib/063_MerkleProof.mv | Bin 0 -> 369 bytes .../latest/stdlib/064_MerkleNFTDistributor.mv | Bin 0 -> 1338 bytes .../latest/stdlib/065_IdentifierNFT.mv | Bin 0 -> 1493 bytes .../compiled/latest/stdlib/066_GenesisNFT.mv | Bin 0 -> 1242 bytes .../latest/stdlib/067_StdlibUpgradeScripts.mv | Bin 0 -> 1943 bytes .../compiled/latest/stdlib/068_Genesis.mv | Bin 0 -> 3629 bytes .../latest/stdlib/069_GenesisNFTScripts.mv | Bin 0 -> 125 bytes .../latest/stdlib/070_IdentifierNFTScripts.mv | Bin 0 -> 204 bytes .../latest/stdlib/071_MintDaoProposal.mv | Bin 0 -> 681 bytes .../latest/stdlib/072_ModuleUpgradeScripts.mv | Bin 0 -> 901 bytes .../compiled/latest/stdlib/073_NFTGallery.mv | Bin 0 -> 2260 bytes .../latest/stdlib/074_NFTGalleryScripts.mv | Bin 0 -> 271 bytes .../latest/stdlib/075_OnChainConfigScripts.mv | Bin 0 -> 1130 bytes .../stdlib/076_PriceOracleAggregator.mv | Bin 0 -> 570 bytes .../latest/stdlib/077_PriceOracleScripts.mv | Bin 0 -> 274 bytes .../compiled/latest/stdlib/078_Secp256k1.mv | Bin 0 -> 633 bytes .../compiled/latest/stdlib/079_Signature.mv | Bin 0 -> 430 bytes .../stdlib/080_SharedEd25519PublicKey.mv | Bin 0 -> 615 bytes .../compiled/latest/stdlib/081_SimpleMap.mv | Bin 0 -> 1281 bytes .../latest/stdlib/082_StructuredHash.mv | Bin 0 -> 270 bytes .../latest/stdlib/083_StarcoinVerifier.mv | Bin 0 -> 1988 bytes .../compiled/latest/stdlib/084_String.mv | Bin 0 -> 936 bytes vm/stdlib/compiled/latest/stdlib/085_Table.mv | Bin 0 -> 1107 bytes .../latest/stdlib/086_TransactionTimeout.mv | Bin 0 -> 293 bytes .../latest/stdlib/087_TransactionManager.mv | Bin 0 -> 1307 bytes .../latest/stdlib/088_TransferScripts.mv | Bin 0 -> 777 bytes .../latest/stdlib/089_TreasuryScripts.mv | Bin 0 -> 892 bytes .../compiled/latest/stdlib/090_TypeInfo.mv | Bin 0 -> 341 bytes vm/stdlib/compiled/latest/stdlib/091_U256.mv | Bin 0 -> 1196 bytes .../latest/stdlib/092_YieldFarming.mv | Bin 0 -> 1610 bytes .../latest/stdlib/093_YieldFarmingV2.mv | Bin 0 -> 3429 bytes vm/types/src/dag_block_metadata.rs | 146 ++++ 174 files changed, 7415 insertions(+), 4 deletions(-) create mode 100644 consensus/src/consensusdb/access.rs create mode 100644 consensus/src/consensusdb/cache.rs create mode 100644 consensus/src/consensusdb/consensus_ghostdag.rs create mode 100644 consensus/src/consensusdb/consensus_header.rs create mode 100644 consensus/src/consensusdb/consensus_reachability.rs create mode 100644 consensus/src/consensusdb/consensus_relations.rs create mode 100644 consensus/src/consensusdb/db.rs create mode 100644 consensus/src/consensusdb/error.rs create mode 100644 consensus/src/consensusdb/item.rs create mode 100644 consensus/src/consensusdb/mod.rs create mode 100644 consensus/src/consensusdb/schema.rs create mode 100644 consensus/src/consensusdb/writer.rs create mode 100644 consensus/src/dag/blockdag.rs create mode 100644 consensus/src/dag/ghostdag/mergeset.rs create mode 100644 consensus/src/dag/ghostdag/mod.rs create mode 100644 consensus/src/dag/ghostdag/protocol.rs create mode 100644 consensus/src/dag/ghostdag/util.rs create mode 100644 consensus/src/dag/mod.rs create mode 100644 consensus/src/dag/reachability/extensions.rs create mode 100644 consensus/src/dag/reachability/inquirer.rs create mode 100644 consensus/src/dag/reachability/mod.rs create mode 100644 consensus/src/dag/reachability/reachability_service.rs create mode 100644 consensus/src/dag/reachability/reindex.rs create mode 100644 consensus/src/dag/reachability/relations_service.rs create mode 100644 consensus/src/dag/reachability/tests.rs create mode 100644 consensus/src/dag/reachability/tree.rs create mode 100644 consensus/src/dag/types/ghostdata.rs create mode 100644 consensus/src/dag/types/interval.rs create mode 100644 consensus/src/dag/types/mod.rs create mode 100644 consensus/src/dag/types/ordering.rs create mode 100644 consensus/src/dag/types/perf.rs create mode 100644 consensus/src/dag/types/reachability.rs create mode 100644 consensus/src/dag/types/trusted.rs create mode 100644 network-rpc/api/src/dag_protocol.rs create mode 100644 storage/src/flexi_dag/mod.rs create mode 100644 storage/src/tests/test_dag.rs create mode 100644 sync/src/block_connector/test_write_dag_block_chain.rs create mode 100644 sync/src/tasks/sync_dag_accumulator_task.rs create mode 100644 sync/src/tasks/sync_dag_block_task.rs create mode 100644 sync/src/tasks/sync_dag_full_task.rs create mode 100644 sync/src/tasks/sync_dag_protocol_trait.rs create mode 100644 sync/src/tasks/sync_find_ancestor_task.rs create mode 100644 types/src/blockhash.rs create mode 100644 types/src/header.rs create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/053_EventUtil.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/056_GenesisSignerCapability.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/057_Oracle.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/059_STCUSDOracle.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/061_NFT.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/062_LanguageVersion.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/063_MerkleProof.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/066_GenesisNFT.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/067_StdlibUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/068_Genesis.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/070_IdentifierNFTScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/071_MintDaoProposal.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/075_OnChainConfigScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/076_PriceOracleAggregator.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/082_StructuredHash.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/084_String.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/086_TransactionTimeout.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/090_TypeInfo.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/092_YieldFarming.mv create mode 100644 vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv create mode 100644 vm/stdlib/compiled/12/stdlib/052_Epoch.mv create mode 100644 vm/stdlib/compiled/12/stdlib/053_EventUtil.mv create mode 100644 vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv create mode 100644 vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv create mode 100644 vm/stdlib/compiled/12/stdlib/056_GenesisSignerCapability.mv create mode 100644 vm/stdlib/compiled/12/stdlib/057_Oracle.mv create mode 100644 vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv create mode 100644 vm/stdlib/compiled/12/stdlib/059_STCUSDOracle.mv create mode 100644 vm/stdlib/compiled/12/stdlib/060_Offer.mv create mode 100644 vm/stdlib/compiled/12/stdlib/061_NFT.mv create mode 100644 vm/stdlib/compiled/12/stdlib/062_LanguageVersion.mv create mode 100644 vm/stdlib/compiled/12/stdlib/063_MerkleProof.mv create mode 100644 vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv create mode 100644 vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv create mode 100644 vm/stdlib/compiled/12/stdlib/066_GenesisNFT.mv create mode 100644 vm/stdlib/compiled/12/stdlib/067_StdlibUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/068_Genesis.mv create mode 100644 vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/070_IdentifierNFTScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/071_MintDaoProposal.mv create mode 100644 vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv create mode 100644 vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/075_OnChainConfigScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/076_PriceOracleAggregator.mv create mode 100644 vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv create mode 100644 vm/stdlib/compiled/12/stdlib/079_Signature.mv create mode 100644 vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv create mode 100644 vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv create mode 100644 vm/stdlib/compiled/12/stdlib/082_StructuredHash.mv create mode 100644 vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv create mode 100644 vm/stdlib/compiled/12/stdlib/084_String.mv create mode 100644 vm/stdlib/compiled/12/stdlib/085_Table.mv create mode 100644 vm/stdlib/compiled/12/stdlib/086_TransactionTimeout.mv create mode 100644 vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv create mode 100644 vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv create mode 100644 vm/stdlib/compiled/12/stdlib/090_TypeInfo.mv create mode 100644 vm/stdlib/compiled/12/stdlib/091_U256.mv create mode 100644 vm/stdlib/compiled/12/stdlib/092_YieldFarming.mv create mode 100644 vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/052_Epoch.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/053_EventUtil.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/056_GenesisSignerCapability.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/057_Oracle.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/059_STCUSDOracle.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/060_Offer.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/061_NFT.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/062_LanguageVersion.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/063_MerkleProof.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/066_GenesisNFT.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/067_StdlibUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/068_Genesis.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/070_IdentifierNFTScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/071_MintDaoProposal.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/075_OnChainConfigScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/076_PriceOracleAggregator.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/079_Signature.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/082_StructuredHash.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/084_String.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/085_Table.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/086_TransactionTimeout.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/090_TypeInfo.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/091_U256.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/092_YieldFarming.mv create mode 100644 vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv create mode 100644 vm/types/src/dag_block_metadata.rs diff --git a/consensus/src/consensusdb/access.rs b/consensus/src/consensusdb/access.rs new file mode 100644 index 0000000000..e46e85acfe --- /dev/null +++ b/consensus/src/consensusdb/access.rs @@ -0,0 +1,199 @@ +use super::{cache::DagCache, db::DBStorage, error::StoreError}; + +use super::prelude::DbWriter; +use super::schema::{KeyCodec, Schema, ValueCodec}; +use itertools::Itertools; +use rocksdb::{Direction, IteratorMode, ReadOptions}; +use starcoin_storage::storage::RawDBStorage; +use std::{ + collections::hash_map::RandomState, error::Error, hash::BuildHasher, marker::PhantomData, + sync::Arc, +}; + +/// A concurrent DB store access with typed caching. +#[derive(Clone)] +pub struct CachedDbAccess { + db: Arc, + + // Cache + cache: DagCache, + + _phantom: PhantomData, +} + +impl CachedDbAccess +where + R: BuildHasher + Default, +{ + pub fn new(db: Arc, cache_size: u64) -> Self { + Self { + db, + cache: DagCache::new_with_capacity(cache_size), + _phantom: Default::default(), + } + } + + pub fn read_from_cache(&self, key: S::Key) -> Option { + self.cache.get(&key) + } + + pub fn has(&self, key: S::Key) -> Result { + Ok(self.cache.contains_key(&key) + || self + .db + .raw_get_pinned_cf(S::COLUMN_FAMILY, key.encode_key().unwrap()) + .map_err(|_| StoreError::CFNotExist(S::COLUMN_FAMILY.to_string()))? + .is_some()) + } + + pub fn read(&self, key: S::Key) -> Result { + if let Some(data) = self.cache.get(&key) { + Ok(data) + } else if let Some(slice) = self + .db + .raw_get_pinned_cf(S::COLUMN_FAMILY, key.encode_key().unwrap()) + .map_err(|_| StoreError::CFNotExist(S::COLUMN_FAMILY.to_string()))? + { + let data = S::Value::decode_value(slice.as_ref()) + .map_err(|o| StoreError::DecodeError(o.to_string()))?; + self.cache.insert(key, data.clone()); + Ok(data) + } else { + Err(StoreError::KeyNotFound("".to_string())) + } + } + + pub fn iterator( + &self, + ) -> Result, S::Value), Box>> + '_, StoreError> + { + let db_iterator = self + .db + .raw_iterator_cf_opt( + S::COLUMN_FAMILY, + IteratorMode::Start, + ReadOptions::default(), + ) + .map_err(|e| StoreError::CFNotExist(e.to_string()))?; + + Ok(db_iterator.map(|iter_result| match iter_result { + Ok((key, data_bytes)) => match S::Value::decode_value(&data_bytes) { + Ok(data) => Ok((key, data)), + Err(e) => Err(e.into()), + }, + Err(e) => Err(e.into()), + })) + } + + pub fn write( + &self, + mut writer: impl DbWriter, + key: S::Key, + data: S::Value, + ) -> Result<(), StoreError> { + writer.put::(&key, &data)?; + self.cache.insert(key, data); + Ok(()) + } + + pub fn write_many( + &self, + mut writer: impl DbWriter, + iter: &mut (impl Iterator + Clone), + ) -> Result<(), StoreError> { + for (key, data) in iter { + writer.put::(&key, &data)?; + self.cache.insert(key, data); + } + Ok(()) + } + + /// Write directly from an iterator and do not cache any data. NOTE: this action also clears the cache + pub fn write_many_without_cache( + &self, + mut writer: impl DbWriter, + iter: &mut impl Iterator, + ) -> Result<(), StoreError> { + for (key, data) in iter { + writer.put::(&key, &data)?; + } + // The cache must be cleared in order to avoid invalidated entries + self.cache.remove_all(); + Ok(()) + } + + pub fn delete(&self, mut writer: impl DbWriter, key: S::Key) -> Result<(), StoreError> { + self.cache.remove(&key); + writer.delete::(&key)?; + Ok(()) + } + + pub fn delete_many( + &self, + mut writer: impl DbWriter, + key_iter: &mut (impl Iterator + Clone), + ) -> Result<(), StoreError> { + let key_iter_clone = key_iter.clone(); + self.cache.remove_many(key_iter); + for key in key_iter_clone { + writer.delete::(&key)?; + } + Ok(()) + } + + pub fn delete_all(&self, mut writer: impl DbWriter) -> Result<(), StoreError> { + self.cache.remove_all(); + let keys = self + .db + .raw_iterator_cf_opt( + S::COLUMN_FAMILY, + IteratorMode::Start, + ReadOptions::default(), + ) + .map_err(|e| StoreError::CFNotExist(e.to_string()))? + .map(|iter_result| match iter_result { + Ok((key, _)) => Ok::<_, rocksdb::Error>(key), + Err(e) => Err(e), + }) + .collect_vec(); + for key in keys { + writer.delete::(&S::Key::decode_key(&key?)?)?; + } + Ok(()) + } + + /// A dynamic iterator that can iterate through a specific prefix, and from a certain start point. + //TODO: loop and chain iterators for multi-prefix iterator. + pub fn seek_iterator( + &self, + seek_from: Option, // iter whole range if None + limit: usize, // amount to take. + skip_first: bool, // skips the first value, (useful in conjunction with the seek-key, as to not re-retrieve). + ) -> Result, S::Value), Box>> + '_, StoreError> + { + let read_opts = ReadOptions::default(); + let mut db_iterator = match seek_from { + Some(seek_key) => self.db.raw_iterator_cf_opt( + S::COLUMN_FAMILY, + IteratorMode::From(seek_key.encode_key()?.as_slice(), Direction::Forward), + read_opts, + ), + None => self + .db + .raw_iterator_cf_opt(S::COLUMN_FAMILY, IteratorMode::Start, read_opts), + } + .map_err(|e| StoreError::CFNotExist(e.to_string()))?; + + if skip_first { + db_iterator.next(); + } + + Ok(db_iterator.take(limit).map(move |item| match item { + Ok((key_bytes, value_bytes)) => match S::Value::decode_value(value_bytes.as_ref()) { + Ok(value) => Ok((key_bytes, value)), + Err(err) => Err(err.into()), + }, + Err(err) => Err(err.into()), + })) + } +} diff --git a/consensus/src/consensusdb/cache.rs b/consensus/src/consensusdb/cache.rs new file mode 100644 index 0000000000..e2d5de0c3c --- /dev/null +++ b/consensus/src/consensusdb/cache.rs @@ -0,0 +1,44 @@ +use core::hash::Hash; +use starcoin_storage::cache_storage::GCacheStorage; +use std::sync::Arc; + +#[derive(Clone)] +pub struct DagCache { + cache: Arc>, +} + +impl DagCache +where + K: Hash + Eq + Default, + V: Default + Clone, +{ + pub(crate) fn new_with_capacity(size: u64) -> Self { + Self { + cache: Arc::new(GCacheStorage::new_with_capacity(size as usize, None)), + } + } + + pub(crate) fn get(&self, key: &K) -> Option { + self.cache.get_inner(key) + } + + pub(crate) fn contains_key(&self, key: &K) -> bool { + self.get(key).is_some() + } + + pub(crate) fn insert(&self, key: K, data: V) { + self.cache.put_inner(key, data); + } + + pub(crate) fn remove(&self, key: &K) { + self.cache.remove_inner(key); + } + + pub(crate) fn remove_many(&self, key_iter: &mut impl Iterator) { + key_iter.for_each(|k| self.remove(&k)); + } + + pub(crate) fn remove_all(&self) { + self.cache.remove_all(); + } +} diff --git a/consensus/src/consensusdb/consensus_ghostdag.rs b/consensus/src/consensusdb/consensus_ghostdag.rs new file mode 100644 index 0000000000..a6746d9eb5 --- /dev/null +++ b/consensus/src/consensusdb/consensus_ghostdag.rs @@ -0,0 +1,512 @@ +use super::schema::{KeyCodec, ValueCodec}; +use super::{ + db::DBStorage, + error::StoreError, + prelude::{CachedDbAccess, DirectDbWriter}, + writer::BatchDbWriter, +}; +use crate::define_schema; +use starcoin_types::blockhash::{ + BlockHashMap, BlockHashes, BlockLevel, BlueWorkType, HashKTypeMap, +}; + +use crate::dag::types::{ + ghostdata::{CompactGhostdagData, GhostdagData}, + ordering::SortableBlock, +}; +use itertools::{ + EitherOrBoth::{Both, Left, Right}, + Itertools, +}; +use rocksdb::WriteBatch; +use starcoin_crypto::HashValue as Hash; +use std::{cell::RefCell, cmp, iter::once, sync::Arc}; + +pub trait GhostdagStoreReader { + fn get_blue_score(&self, hash: Hash) -> Result; + fn get_blue_work(&self, hash: Hash) -> Result; + fn get_selected_parent(&self, hash: Hash) -> Result; + fn get_mergeset_blues(&self, hash: Hash) -> Result; + fn get_mergeset_reds(&self, hash: Hash) -> Result; + fn get_blues_anticone_sizes(&self, hash: Hash) -> Result; + + /// Returns full block data for the requested hash + fn get_data(&self, hash: Hash) -> Result, StoreError>; + + fn get_compact_data(&self, hash: Hash) -> Result; + + /// Check if the store contains data for the requested hash + fn has(&self, hash: Hash) -> Result; +} + +pub trait GhostdagStore: GhostdagStoreReader { + /// Insert GHOSTDAG data for block `hash` into the store. Note that GHOSTDAG data + /// is added once and never modified, so no need for specific setters for each element. + /// Additionally, this means writes are semantically "append-only", which is why + /// we can keep the `insert` method non-mutable on self. See "Parallel Processing.md" for an overview. + fn insert(&self, hash: Hash, data: Arc) -> Result<(), StoreError>; +} + +pub struct GhostDagDataWrapper(GhostdagData); + +impl From for GhostDagDataWrapper { + fn from(value: GhostdagData) -> Self { + Self(value) + } +} + +impl GhostDagDataWrapper { + /// Returns an iterator to the mergeset in ascending blue work order (tie-breaking by hash) + pub fn ascending_mergeset_without_selected_parent<'a>( + &'a self, + store: &'a (impl GhostdagStoreReader + ?Sized), + ) -> impl Iterator> + '_ { + self.0 + .mergeset_blues + .iter() + .skip(1) // Skip the selected parent + .cloned() + .map(|h| { + store + .get_blue_work(h) + .map(|blue| SortableBlock::new(h, blue)) + }) + .merge_join_by( + self.0 + .mergeset_reds + .iter() + .cloned() + .map(|h| store.get_blue_work(h).map(|red| SortableBlock::new(h, red))), + |a, b| match (a, b) { + (Ok(a), Ok(b)) => a.cmp(b), + (Err(_), Ok(_)) => cmp::Ordering::Less, // select left Err node + (Ok(_), Err(_)) => cmp::Ordering::Greater, // select right Err node + (Err(_), Err(_)) => cmp::Ordering::Equal, // remove both Err nodes + }, + ) + .map(|r| match r { + Left(b) | Right(b) => b, + Both(c, _) => Err(StoreError::DAGDupBlocksError(format!("{c:?}"))), + }) + } + + /// Returns an iterator to the mergeset in descending blue work order (tie-breaking by hash) + pub fn descending_mergeset_without_selected_parent<'a>( + &'a self, + store: &'a (impl GhostdagStoreReader + ?Sized), + ) -> impl Iterator> + '_ { + self.0 + .mergeset_blues + .iter() + .skip(1) // Skip the selected parent + .rev() // Reverse since blues and reds are stored with ascending blue work order + .cloned() + .map(|h| { + store + .get_blue_work(h) + .map(|blue| SortableBlock::new(h, blue)) + }) + .merge_join_by( + self.0 + .mergeset_reds + .iter() + .rev() // Reverse + .cloned() + .map(|h| store.get_blue_work(h).map(|red| SortableBlock::new(h, red))), + |a, b| match (b, a) { + (Ok(b), Ok(a)) => b.cmp(a), + (Err(_), Ok(_)) => cmp::Ordering::Less, // select left Err node + (Ok(_), Err(_)) => cmp::Ordering::Greater, // select right Err node + (Err(_), Err(_)) => cmp::Ordering::Equal, // select both Err nodes + }, // Reverse + ) + .map(|r| match r { + Left(b) | Right(b) => b, + Both(c, _) => Err(StoreError::DAGDupBlocksError(format!("{c:?}"))), + }) + } + + /// Returns an iterator to the mergeset in topological consensus order -- starting with the selected parent, + /// and adding the mergeset in increasing blue work order. Note that this is a topological order even though + /// the selected parent has highest blue work by def -- since the mergeset is in its anticone. + pub fn consensus_ordered_mergeset<'a>( + &'a self, + store: &'a (impl GhostdagStoreReader + ?Sized), + ) -> impl Iterator> + '_ { + once(Ok(self.0.selected_parent)).chain( + self.ascending_mergeset_without_selected_parent(store) + .map(|s| s.map(|s| s.hash)), + ) + } + + /// Returns an iterator to the mergeset in topological consensus order without the selected parent + pub fn consensus_ordered_mergeset_without_selected_parent<'a>( + &'a self, + store: &'a (impl GhostdagStoreReader + ?Sized), + ) -> impl Iterator> + '_ { + self.ascending_mergeset_without_selected_parent(store) + .map(|s| s.map(|s| s.hash)) + } +} + +pub(crate) const GHOST_DAG_STORE_CF: &str = "block-ghostdag-data"; +pub(crate) const COMPACT_GHOST_DAG_STORE_CF: &str = "compact-block-ghostdag-data"; + +define_schema!(GhostDag, Hash, Arc, GHOST_DAG_STORE_CF); +define_schema!( + CompactGhostDag, + Hash, + CompactGhostdagData, + COMPACT_GHOST_DAG_STORE_CF +); + +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for Arc { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(&self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for CompactGhostdagData { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(&self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +/// A DB + cache implementation of `GhostdagStore` trait, with concurrency support. +#[derive(Clone)] +pub struct DbGhostdagStore { + db: Arc, + level: BlockLevel, + access: CachedDbAccess, + compact_access: CachedDbAccess, +} + +impl DbGhostdagStore { + pub fn new(db: Arc, level: BlockLevel, cache_size: u64) -> Self { + Self { + db: Arc::clone(&db), + level, + access: CachedDbAccess::new(db.clone(), cache_size), + compact_access: CachedDbAccess::new(db, cache_size), + } + } + + pub fn clone_with_new_cache(&self, cache_size: u64) -> Self { + Self::new(Arc::clone(&self.db), self.level, cache_size) + } + + pub fn insert_batch( + &self, + batch: &mut WriteBatch, + hash: Hash, + data: &Arc, + ) -> Result<(), StoreError> { + if self.access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.access + .write(BatchDbWriter::new(batch), hash, data.clone())?; + self.compact_access.write( + BatchDbWriter::new(batch), + hash, + CompactGhostdagData { + blue_score: data.blue_score, + blue_work: data.blue_work, + selected_parent: data.selected_parent, + }, + )?; + Ok(()) + } +} + +impl GhostdagStoreReader for DbGhostdagStore { + fn get_blue_score(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.blue_score) + } + + fn get_blue_work(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.blue_work) + } + + fn get_selected_parent(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.selected_parent) + } + + fn get_mergeset_blues(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.access.read(hash)?.mergeset_blues)) + } + + fn get_mergeset_reds(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.access.read(hash)?.mergeset_reds)) + } + + fn get_blues_anticone_sizes(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.access.read(hash)?.blues_anticone_sizes)) + } + + fn get_data(&self, hash: Hash) -> Result, StoreError> { + self.access.read(hash) + } + + fn get_compact_data(&self, hash: Hash) -> Result { + self.compact_access.read(hash) + } + + fn has(&self, hash: Hash) -> Result { + self.access.has(hash) + } +} + +impl GhostdagStore for DbGhostdagStore { + fn insert(&self, hash: Hash, data: Arc) -> Result<(), StoreError> { + if self.access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.access + .write(DirectDbWriter::new(&self.db), hash, data.clone())?; + if self.compact_access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.compact_access.write( + DirectDbWriter::new(&self.db), + hash, + CompactGhostdagData { + blue_score: data.blue_score, + blue_work: data.blue_work, + selected_parent: data.selected_parent, + }, + )?; + Ok(()) + } +} + +/// An in-memory implementation of `GhostdagStore` trait to be used for tests. +/// Uses `RefCell` for interior mutability in order to workaround `insert` +/// being non-mutable. +pub struct MemoryGhostdagStore { + blue_score_map: RefCell>, + blue_work_map: RefCell>, + selected_parent_map: RefCell>, + mergeset_blues_map: RefCell>, + mergeset_reds_map: RefCell>, + blues_anticone_sizes_map: RefCell>, +} + +impl MemoryGhostdagStore { + pub fn new() -> Self { + Self { + blue_score_map: RefCell::new(BlockHashMap::new()), + blue_work_map: RefCell::new(BlockHashMap::new()), + selected_parent_map: RefCell::new(BlockHashMap::new()), + mergeset_blues_map: RefCell::new(BlockHashMap::new()), + mergeset_reds_map: RefCell::new(BlockHashMap::new()), + blues_anticone_sizes_map: RefCell::new(BlockHashMap::new()), + } + } +} + +impl Default for MemoryGhostdagStore { + fn default() -> Self { + Self::new() + } +} + +impl GhostdagStore for MemoryGhostdagStore { + fn insert(&self, hash: Hash, data: Arc) -> Result<(), StoreError> { + if self.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.blue_score_map + .borrow_mut() + .insert(hash, data.blue_score); + self.blue_work_map.borrow_mut().insert(hash, data.blue_work); + self.selected_parent_map + .borrow_mut() + .insert(hash, data.selected_parent); + self.mergeset_blues_map + .borrow_mut() + .insert(hash, data.mergeset_blues.clone()); + self.mergeset_reds_map + .borrow_mut() + .insert(hash, data.mergeset_reds.clone()); + self.blues_anticone_sizes_map + .borrow_mut() + .insert(hash, data.blues_anticone_sizes.clone()); + Ok(()) + } +} + +impl GhostdagStoreReader for MemoryGhostdagStore { + fn get_blue_score(&self, hash: Hash) -> Result { + match self.blue_score_map.borrow().get(&hash) { + Some(blue_score) => Ok(*blue_score), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_blue_work(&self, hash: Hash) -> Result { + match self.blue_work_map.borrow().get(&hash) { + Some(blue_work) => Ok(*blue_work), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_selected_parent(&self, hash: Hash) -> Result { + match self.selected_parent_map.borrow().get(&hash) { + Some(selected_parent) => Ok(*selected_parent), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_mergeset_blues(&self, hash: Hash) -> Result { + match self.mergeset_blues_map.borrow().get(&hash) { + Some(mergeset_blues) => Ok(BlockHashes::clone(mergeset_blues)), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_mergeset_reds(&self, hash: Hash) -> Result { + match self.mergeset_reds_map.borrow().get(&hash) { + Some(mergeset_reds) => Ok(BlockHashes::clone(mergeset_reds)), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_blues_anticone_sizes(&self, hash: Hash) -> Result { + match self.blues_anticone_sizes_map.borrow().get(&hash) { + Some(sizes) => Ok(HashKTypeMap::clone(sizes)), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_data(&self, hash: Hash) -> Result, StoreError> { + if !self.has(hash)? { + return Err(StoreError::KeyNotFound(hash.to_string())); + } + Ok(Arc::new(GhostdagData::new( + self.blue_score_map.borrow()[&hash], + self.blue_work_map.borrow()[&hash], + self.selected_parent_map.borrow()[&hash], + self.mergeset_blues_map.borrow()[&hash].clone(), + self.mergeset_reds_map.borrow()[&hash].clone(), + self.blues_anticone_sizes_map.borrow()[&hash].clone(), + ))) + } + + fn get_compact_data(&self, hash: Hash) -> Result { + Ok(self.get_data(hash)?.to_compact()) + } + + fn has(&self, hash: Hash) -> Result { + Ok(self.blue_score_map.borrow().contains_key(&hash)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use starcoin_types::blockhash::BlockHashSet; + use std::iter::once; + + #[test] + fn test_mergeset_iterators() { + let store = MemoryGhostdagStore::new(); + + let factory = |w: u64| { + Arc::new(GhostdagData { + blue_score: Default::default(), + blue_work: w.into(), + selected_parent: Default::default(), + mergeset_blues: Default::default(), + mergeset_reds: Default::default(), + blues_anticone_sizes: Default::default(), + }) + }; + + // Blues + store.insert(1.into(), factory(2)).unwrap(); + store.insert(2.into(), factory(7)).unwrap(); + store.insert(3.into(), factory(11)).unwrap(); + + // Reds + store.insert(4.into(), factory(4)).unwrap(); + store.insert(5.into(), factory(9)).unwrap(); + store.insert(6.into(), factory(11)).unwrap(); // Tie-breaking case + + let mut data = GhostdagData::new_with_selected_parent(1.into(), 5); + data.add_blue(2.into(), Default::default(), &Default::default()); + data.add_blue(3.into(), Default::default(), &Default::default()); + + data.add_red(4.into()); + data.add_red(5.into()); + data.add_red(6.into()); + + let wrapper: GhostDagDataWrapper = data.clone().into(); + + let mut expected: Vec = vec![4.into(), 2.into(), 5.into(), 3.into(), 6.into()]; + assert_eq!( + expected, + wrapper + .ascending_mergeset_without_selected_parent(&store) + .filter_map(|b| b.map(|b| b.hash).ok()) + .collect::>() + ); + + itertools::assert_equal( + once(1.into()).chain(expected.iter().cloned()), + wrapper + .consensus_ordered_mergeset(&store) + .filter_map(|b| b.ok()), + ); + + expected.reverse(); + assert_eq!( + expected, + wrapper + .descending_mergeset_without_selected_parent(&store) + .filter_map(|b| b.map(|b| b.hash).ok()) + .collect::>() + ); + + // Use sets since the below functions have no order guarantee + let expected = BlockHashSet::from_iter([4.into(), 2.into(), 5.into(), 3.into(), 6.into()]); + assert_eq!( + expected, + data.unordered_mergeset_without_selected_parent() + .collect::() + ); + + let expected = + BlockHashSet::from_iter([1.into(), 4.into(), 2.into(), 5.into(), 3.into(), 6.into()]); + assert_eq!( + expected, + data.unordered_mergeset().collect::() + ); + } +} diff --git a/consensus/src/consensusdb/consensus_header.rs b/consensus/src/consensusdb/consensus_header.rs new file mode 100644 index 0000000000..6a9c06fbdc --- /dev/null +++ b/consensus/src/consensusdb/consensus_header.rs @@ -0,0 +1,216 @@ +use super::schema::{KeyCodec, ValueCodec}; +use super::{ + db::DBStorage, + error::{StoreError, StoreResult}, + prelude::CachedDbAccess, + writer::{BatchDbWriter, DirectDbWriter}, +}; +use crate::define_schema; +use rocksdb::WriteBatch; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::{ + blockhash::BlockLevel, + header::{CompactHeaderData, ConsensusHeader, DagHeader, HeaderWithBlockLevel}, + U256, +}; +use std::sync::Arc; + +pub trait HeaderStoreReader { + fn get_daa_score(&self, hash: Hash) -> Result; + fn get_blue_score(&self, hash: Hash) -> Result; + fn get_timestamp(&self, hash: Hash) -> Result; + fn get_difficulty(&self, hash: Hash) -> Result; + fn get_header(&self, hash: Hash) -> Result, StoreError>; + fn get_header_with_block_level(&self, hash: Hash) -> Result; + fn get_compact_header_data(&self, hash: Hash) -> Result; +} + +pub trait HeaderStore: HeaderStoreReader { + // This is append only + fn insert( + &self, + hash: Hash, + header: Arc, + block_level: BlockLevel, + ) -> Result<(), StoreError>; +} + +pub(crate) const HEADERS_STORE_CF: &str = "headers-store"; +pub(crate) const COMPACT_HEADER_DATA_STORE_CF: &str = "compact-header-data"; + +define_schema!(BlockHeader, Hash, HeaderWithBlockLevel, HEADERS_STORE_CF); +define_schema!( + CompactBlockHeader, + Hash, + CompactHeaderData, + COMPACT_HEADER_DATA_STORE_CF +); + +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for HeaderWithBlockLevel { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(&self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for CompactHeaderData { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(&self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +/// A DB + cache implementation of `HeaderStore` trait, with concurrency support. +#[derive(Clone)] +pub struct DbHeadersStore { + db: Arc, + headers_access: CachedDbAccess, + compact_headers_access: CachedDbAccess, +} + +impl DbHeadersStore { + pub fn new(db: Arc, cache_size: u64) -> Self { + Self { + db: Arc::clone(&db), + headers_access: CachedDbAccess::new(db.clone(), cache_size), + compact_headers_access: CachedDbAccess::new(db, cache_size), + } + } + + pub fn clone_with_new_cache(&self, cache_size: u64) -> Self { + Self::new(Arc::clone(&self.db), cache_size) + } + + pub fn has(&self, hash: Hash) -> StoreResult { + self.headers_access.has(hash) + } + + pub fn get_header(&self, hash: Hash) -> Result { + let result = self.headers_access.read(hash)?; + Ok((*result.header).clone()) + } + + pub fn insert_batch( + &self, + batch: &mut WriteBatch, + hash: Hash, + header: Arc, + block_level: BlockLevel, + ) -> Result<(), StoreError> { + if self.headers_access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.headers_access.write( + BatchDbWriter::new(batch), + hash, + HeaderWithBlockLevel { + header: header.clone(), + block_level, + }, + )?; + self.compact_headers_access.write( + BatchDbWriter::new(batch), + hash, + CompactHeaderData { + timestamp: header.timestamp(), + difficulty: header.difficulty(), + }, + )?; + Ok(()) + } +} + +impl HeaderStoreReader for DbHeadersStore { + fn get_daa_score(&self, _hash: Hash) -> Result { + unimplemented!() + } + + fn get_blue_score(&self, _hash: Hash) -> Result { + unimplemented!() + } + + fn get_timestamp(&self, hash: Hash) -> Result { + if let Some(header_with_block_level) = self.headers_access.read_from_cache(hash) { + return Ok(header_with_block_level.header.timestamp()); + } + Ok(self.compact_headers_access.read(hash)?.timestamp) + } + + fn get_difficulty(&self, hash: Hash) -> Result { + if let Some(header_with_block_level) = self.headers_access.read_from_cache(hash) { + return Ok(header_with_block_level.header.difficulty()); + } + Ok(self.compact_headers_access.read(hash)?.difficulty) + } + + fn get_header(&self, hash: Hash) -> Result, StoreError> { + Ok(self.headers_access.read(hash)?.header) + } + + fn get_header_with_block_level(&self, hash: Hash) -> Result { + self.headers_access.read(hash) + } + + fn get_compact_header_data(&self, hash: Hash) -> Result { + if let Some(header_with_block_level) = self.headers_access.read_from_cache(hash) { + return Ok(CompactHeaderData { + timestamp: header_with_block_level.header.timestamp(), + difficulty: header_with_block_level.header.difficulty(), + }); + } + self.compact_headers_access.read(hash) + } +} + +impl HeaderStore for DbHeadersStore { + fn insert( + &self, + hash: Hash, + header: Arc, + block_level: u8, + ) -> Result<(), StoreError> { + if self.headers_access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + self.compact_headers_access.write( + DirectDbWriter::new(&self.db), + hash, + CompactHeaderData { + timestamp: header.timestamp(), + difficulty: header.difficulty(), + }, + )?; + self.headers_access.write( + DirectDbWriter::new(&self.db), + hash, + HeaderWithBlockLevel { + header, + block_level, + }, + )?; + Ok(()) + } +} diff --git a/consensus/src/consensusdb/consensus_reachability.rs b/consensus/src/consensusdb/consensus_reachability.rs new file mode 100644 index 0000000000..308ffb88a8 --- /dev/null +++ b/consensus/src/consensusdb/consensus_reachability.rs @@ -0,0 +1,540 @@ +use super::{ + db::DBStorage, + prelude::{BatchDbWriter, CachedDbAccess, CachedDbItem, DirectDbWriter, StoreError}, +}; +use starcoin_crypto::HashValue as Hash; +use starcoin_storage::storage::RawDBStorage; + +use crate::{ + dag::types::{interval::Interval, reachability::ReachabilityData}, + define_schema, + schema::{KeyCodec, ValueCodec}, +}; +use starcoin_types::blockhash::{self, BlockHashMap, BlockHashes}; + +use parking_lot::{RwLockUpgradableReadGuard, RwLockWriteGuard}; +use rocksdb::WriteBatch; +use std::{collections::hash_map::Entry::Vacant, sync::Arc}; + +/// Reader API for `ReachabilityStore`. +pub trait ReachabilityStoreReader { + fn has(&self, hash: Hash) -> Result; + fn get_interval(&self, hash: Hash) -> Result; + fn get_parent(&self, hash: Hash) -> Result; + fn get_children(&self, hash: Hash) -> Result; + fn get_future_covering_set(&self, hash: Hash) -> Result; +} + +/// Write API for `ReachabilityStore`. All write functions are deliberately `mut` +/// since reachability writes are not append-only and thus need to be guarded. +pub trait ReachabilityStore: ReachabilityStoreReader { + fn init(&mut self, origin: Hash, capacity: Interval) -> Result<(), StoreError>; + fn insert( + &mut self, + hash: Hash, + parent: Hash, + interval: Interval, + height: u64, + ) -> Result<(), StoreError>; + fn set_interval(&mut self, hash: Hash, interval: Interval) -> Result<(), StoreError>; + fn append_child(&mut self, hash: Hash, child: Hash) -> Result; + fn insert_future_covering_item( + &mut self, + hash: Hash, + fci: Hash, + insertion_index: usize, + ) -> Result<(), StoreError>; + fn get_height(&self, hash: Hash) -> Result; + fn set_reindex_root(&mut self, root: Hash) -> Result<(), StoreError>; + fn get_reindex_root(&self) -> Result; +} + +const REINDEX_ROOT_KEY: &str = "reachability-reindex-root"; +pub(crate) const REACHABILITY_DATA_CF: &str = "reachability-data"; +// TODO: explore perf to see if using fixed-length constants for store prefixes is preferable + +define_schema!( + Reachability, + Hash, + Arc, + REACHABILITY_DATA_CF +); +define_schema!(ReachabilityCache, Vec, Hash, REACHABILITY_DATA_CF); + +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for Arc { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(&self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl KeyCodec for Vec { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Ok(data.to_vec()) + } +} +impl ValueCodec for Hash { + fn encode_value(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_value(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +/// A DB + cache implementation of `ReachabilityStore` trait, with concurrent readers support. +#[derive(Clone)] +pub struct DbReachabilityStore { + db: Arc, + access: CachedDbAccess, + reindex_root: CachedDbItem, +} + +impl DbReachabilityStore { + pub fn new(db: Arc, cache_size: u64) -> Self { + Self::new_with_prefix_end(db, cache_size) + } + + pub fn new_with_alternative_prefix_end(db: Arc, cache_size: u64) -> Self { + Self::new_with_prefix_end(db, cache_size) + } + + fn new_with_prefix_end(db: Arc, cache_size: u64) -> Self { + Self { + db: Arc::clone(&db), + access: CachedDbAccess::new(Arc::clone(&db), cache_size), + reindex_root: CachedDbItem::new(db, REINDEX_ROOT_KEY.as_bytes().to_vec()), + } + } + + pub fn clone_with_new_cache(&self, cache_size: u64) -> Self { + Self::new_with_prefix_end(Arc::clone(&self.db), cache_size) + } +} + +impl ReachabilityStore for DbReachabilityStore { + fn init(&mut self, origin: Hash, capacity: Interval) -> Result<(), StoreError> { + debug_assert!(!self.access.has(origin)?); + + let data = Arc::new(ReachabilityData::new( + Hash::new(blockhash::NONE), + capacity, + 0, + )); + let mut batch = WriteBatch::default(); + self.access + .write(BatchDbWriter::new(&mut batch), origin, data)?; + self.reindex_root + .write(BatchDbWriter::new(&mut batch), &origin)?; + self.db + .raw_write_batch(batch) + .map_err(|e| StoreError::DBIoError(e.to_string()))?; + + Ok(()) + } + + fn insert( + &mut self, + hash: Hash, + parent: Hash, + interval: Interval, + height: u64, + ) -> Result<(), StoreError> { + if self.access.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + let data = Arc::new(ReachabilityData::new(parent, interval, height)); + self.access + .write(DirectDbWriter::new(&self.db), hash, data)?; + Ok(()) + } + + fn set_interval(&mut self, hash: Hash, interval: Interval) -> Result<(), StoreError> { + let mut data = self.access.read(hash)?; + Arc::make_mut(&mut data).interval = interval; + self.access + .write(DirectDbWriter::new(&self.db), hash, data)?; + Ok(()) + } + + fn append_child(&mut self, hash: Hash, child: Hash) -> Result { + let mut data = self.access.read(hash)?; + let height = data.height; + let mut_data = Arc::make_mut(&mut data); + Arc::make_mut(&mut mut_data.children).push(child); + self.access + .write(DirectDbWriter::new(&self.db), hash, data)?; + Ok(height) + } + + fn insert_future_covering_item( + &mut self, + hash: Hash, + fci: Hash, + insertion_index: usize, + ) -> Result<(), StoreError> { + let mut data = self.access.read(hash)?; + let mut_data = Arc::make_mut(&mut data); + Arc::make_mut(&mut mut_data.future_covering_set).insert(insertion_index, fci); + self.access + .write(DirectDbWriter::new(&self.db), hash, data)?; + Ok(()) + } + + fn get_height(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.height) + } + + fn set_reindex_root(&mut self, root: Hash) -> Result<(), StoreError> { + self.reindex_root + .write(DirectDbWriter::new(&self.db), &root) + } + + fn get_reindex_root(&self) -> Result { + self.reindex_root.read() + } +} + +impl ReachabilityStoreReader for DbReachabilityStore { + fn has(&self, hash: Hash) -> Result { + self.access.has(hash) + } + + fn get_interval(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.interval) + } + + fn get_parent(&self, hash: Hash) -> Result { + Ok(self.access.read(hash)?.parent) + } + + fn get_children(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.access.read(hash)?.children)) + } + + fn get_future_covering_set(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.access.read(hash)?.future_covering_set)) + } +} + +pub struct StagingReachabilityStore<'a> { + store_read: RwLockUpgradableReadGuard<'a, DbReachabilityStore>, + staging_writes: BlockHashMap, + staging_reindex_root: Option, +} + +impl<'a> StagingReachabilityStore<'a> { + pub fn new(store_read: RwLockUpgradableReadGuard<'a, DbReachabilityStore>) -> Self { + Self { + store_read, + staging_writes: BlockHashMap::new(), + staging_reindex_root: None, + } + } + + pub fn commit( + self, + batch: &mut WriteBatch, + ) -> Result, StoreError> { + let mut store_write = RwLockUpgradableReadGuard::upgrade(self.store_read); + for (k, v) in self.staging_writes { + let data = Arc::new(v); + store_write + .access + .write(BatchDbWriter::new(batch), k, data)? + } + if let Some(root) = self.staging_reindex_root { + store_write + .reindex_root + .write(BatchDbWriter::new(batch), &root)?; + } + Ok(store_write) + } +} + +impl ReachabilityStore for StagingReachabilityStore<'_> { + fn init(&mut self, origin: Hash, capacity: Interval) -> Result<(), StoreError> { + self.insert(origin, Hash::new(blockhash::NONE), capacity, 0)?; + self.set_reindex_root(origin)?; + Ok(()) + } + + fn insert( + &mut self, + hash: Hash, + parent: Hash, + interval: Interval, + height: u64, + ) -> Result<(), StoreError> { + if self.store_read.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + if let Vacant(e) = self.staging_writes.entry(hash) { + e.insert(ReachabilityData::new(parent, interval, height)); + Ok(()) + } else { + Err(StoreError::KeyAlreadyExists(hash.to_string())) + } + } + + fn set_interval(&mut self, hash: Hash, interval: Interval) -> Result<(), StoreError> { + if let Some(data) = self.staging_writes.get_mut(&hash) { + data.interval = interval; + return Ok(()); + } + + let mut data = (*self.store_read.access.read(hash)?).clone(); + data.interval = interval; + self.staging_writes.insert(hash, data); + + Ok(()) + } + + fn append_child(&mut self, hash: Hash, child: Hash) -> Result { + if let Some(data) = self.staging_writes.get_mut(&hash) { + Arc::make_mut(&mut data.children).push(child); + return Ok(data.height); + } + + let mut data = (*self.store_read.access.read(hash)?).clone(); + let height = data.height; + Arc::make_mut(&mut data.children).push(child); + self.staging_writes.insert(hash, data); + + Ok(height) + } + + fn insert_future_covering_item( + &mut self, + hash: Hash, + fci: Hash, + insertion_index: usize, + ) -> Result<(), StoreError> { + if let Some(data) = self.staging_writes.get_mut(&hash) { + Arc::make_mut(&mut data.future_covering_set).insert(insertion_index, fci); + return Ok(()); + } + + let mut data = (*self.store_read.access.read(hash)?).clone(); + Arc::make_mut(&mut data.future_covering_set).insert(insertion_index, fci); + self.staging_writes.insert(hash, data); + + Ok(()) + } + + fn get_height(&self, hash: Hash) -> Result { + if let Some(data) = self.staging_writes.get(&hash) { + Ok(data.height) + } else { + Ok(self.store_read.access.read(hash)?.height) + } + } + + fn set_reindex_root(&mut self, root: Hash) -> Result<(), StoreError> { + self.staging_reindex_root = Some(root); + Ok(()) + } + + fn get_reindex_root(&self) -> Result { + if let Some(root) = self.staging_reindex_root { + Ok(root) + } else { + Ok(self.store_read.get_reindex_root()?) + } + } +} + +impl ReachabilityStoreReader for StagingReachabilityStore<'_> { + fn has(&self, hash: Hash) -> Result { + Ok(self.staging_writes.contains_key(&hash) || self.store_read.access.has(hash)?) + } + + fn get_interval(&self, hash: Hash) -> Result { + if let Some(data) = self.staging_writes.get(&hash) { + Ok(data.interval) + } else { + Ok(self.store_read.access.read(hash)?.interval) + } + } + + fn get_parent(&self, hash: Hash) -> Result { + if let Some(data) = self.staging_writes.get(&hash) { + Ok(data.parent) + } else { + Ok(self.store_read.access.read(hash)?.parent) + } + } + + fn get_children(&self, hash: Hash) -> Result { + if let Some(data) = self.staging_writes.get(&hash) { + Ok(BlockHashes::clone(&data.children)) + } else { + Ok(BlockHashes::clone( + &self.store_read.access.read(hash)?.children, + )) + } + } + + fn get_future_covering_set(&self, hash: Hash) -> Result { + if let Some(data) = self.staging_writes.get(&hash) { + Ok(BlockHashes::clone(&data.future_covering_set)) + } else { + Ok(BlockHashes::clone( + &self.store_read.access.read(hash)?.future_covering_set, + )) + } + } +} + +pub struct MemoryReachabilityStore { + map: BlockHashMap, + reindex_root: Option, +} + +impl Default for MemoryReachabilityStore { + fn default() -> Self { + Self::new() + } +} + +impl MemoryReachabilityStore { + pub fn new() -> Self { + Self { + map: BlockHashMap::new(), + reindex_root: None, + } + } + + fn get_data_mut(&mut self, hash: Hash) -> Result<&mut ReachabilityData, StoreError> { + match self.map.get_mut(&hash) { + Some(data) => Ok(data), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_data(&self, hash: Hash) -> Result<&ReachabilityData, StoreError> { + match self.map.get(&hash) { + Some(data) => Ok(data), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } +} + +impl ReachabilityStore for MemoryReachabilityStore { + fn init(&mut self, origin: Hash, capacity: Interval) -> Result<(), StoreError> { + self.insert(origin, Hash::new(blockhash::NONE), capacity, 0)?; + self.set_reindex_root(origin)?; + Ok(()) + } + + fn insert( + &mut self, + hash: Hash, + parent: Hash, + interval: Interval, + height: u64, + ) -> Result<(), StoreError> { + if let Vacant(e) = self.map.entry(hash) { + e.insert(ReachabilityData::new(parent, interval, height)); + Ok(()) + } else { + Err(StoreError::KeyAlreadyExists(hash.to_string())) + } + } + + fn set_interval(&mut self, hash: Hash, interval: Interval) -> Result<(), StoreError> { + let data = self.get_data_mut(hash)?; + data.interval = interval; + Ok(()) + } + + fn append_child(&mut self, hash: Hash, child: Hash) -> Result { + let data = self.get_data_mut(hash)?; + Arc::make_mut(&mut data.children).push(child); + Ok(data.height) + } + + fn insert_future_covering_item( + &mut self, + hash: Hash, + fci: Hash, + insertion_index: usize, + ) -> Result<(), StoreError> { + let data = self.get_data_mut(hash)?; + Arc::make_mut(&mut data.future_covering_set).insert(insertion_index, fci); + Ok(()) + } + + fn get_height(&self, hash: Hash) -> Result { + Ok(self.get_data(hash)?.height) + } + + fn set_reindex_root(&mut self, root: Hash) -> Result<(), StoreError> { + self.reindex_root = Some(root); + Ok(()) + } + + fn get_reindex_root(&self) -> Result { + match self.reindex_root { + Some(root) => Ok(root), + None => Err(StoreError::KeyNotFound(REINDEX_ROOT_KEY.to_string())), + } + } +} + +impl ReachabilityStoreReader for MemoryReachabilityStore { + fn has(&self, hash: Hash) -> Result { + Ok(self.map.contains_key(&hash)) + } + + fn get_interval(&self, hash: Hash) -> Result { + Ok(self.get_data(hash)?.interval) + } + + fn get_parent(&self, hash: Hash) -> Result { + Ok(self.get_data(hash)?.parent) + } + + fn get_children(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.get_data(hash)?.children)) + } + + fn get_future_covering_set(&self, hash: Hash) -> Result { + Ok(Arc::clone(&self.get_data(hash)?.future_covering_set)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_store_basics() { + let mut store: Box = Box::new(MemoryReachabilityStore::new()); + let (hash, parent) = (7.into(), 15.into()); + let interval = Interval::maximal(); + store.insert(hash, parent, interval, 5).unwrap(); + let height = store.append_child(hash, 31.into()).unwrap(); + assert_eq!(height, 5); + let children = store.get_children(hash).unwrap(); + println!("{children:?}"); + store.get_interval(7.into()).unwrap(); + println!("{children:?}"); + } +} diff --git a/consensus/src/consensusdb/consensus_relations.rs b/consensus/src/consensusdb/consensus_relations.rs new file mode 100644 index 0000000000..a34c1c049c --- /dev/null +++ b/consensus/src/consensusdb/consensus_relations.rs @@ -0,0 +1,316 @@ +use super::schema::{KeyCodec, ValueCodec}; +use super::{ + db::DBStorage, + prelude::{BatchDbWriter, CachedDbAccess, DirectDbWriter, StoreError}, +}; +use crate::define_schema; +use rocksdb::WriteBatch; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{BlockHashMap, BlockHashes, BlockLevel}; +use std::{collections::hash_map::Entry::Vacant, sync::Arc}; + +/// Reader API for `RelationsStore`. +pub trait RelationsStoreReader { + fn get_parents(&self, hash: Hash) -> Result; + fn get_children(&self, hash: Hash) -> Result; + fn has(&self, hash: Hash) -> Result; +} + +/// Write API for `RelationsStore`. The insert function is deliberately `mut` +/// since it modifies the children arrays for previously added parents which is +/// non-append-only and thus needs to be guarded. +pub trait RelationsStore: RelationsStoreReader { + /// Inserts `parents` into a new store entry for `hash`, and for each `parent ∈ parents` adds `hash` to `parent.children` + fn insert(&mut self, hash: Hash, parents: BlockHashes) -> Result<(), StoreError>; +} + +pub(crate) const PARENTS_CF: &str = "block-parents"; +pub(crate) const CHILDREN_CF: &str = "block-children"; + +define_schema!(RelationParent, Hash, Arc>, PARENTS_CF); +define_schema!(RelationChildren, Hash, Arc>, CHILDREN_CF); + +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl ValueCodec for Arc> { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} +impl KeyCodec for Hash { + fn encode_key(&self) -> Result, StoreError> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Hash::from_slice(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +impl ValueCodec for Arc> { + fn encode_value(&self) -> Result, StoreError> { + bcs_ext::to_bytes(self).map_err(|e| StoreError::EncodeError(e.to_string())) + } + + fn decode_value(data: &[u8]) -> Result { + bcs_ext::from_bytes(data).map_err(|e| StoreError::DecodeError(e.to_string())) + } +} + +/// A DB + cache implementation of `RelationsStore` trait, with concurrent readers support. +#[derive(Clone)] +pub struct DbRelationsStore { + db: Arc, + level: BlockLevel, + parents_access: CachedDbAccess, + children_access: CachedDbAccess, +} + +impl DbRelationsStore { + pub fn new(db: Arc, level: BlockLevel, cache_size: u64) -> Self { + Self { + db: Arc::clone(&db), + level, + parents_access: CachedDbAccess::new(Arc::clone(&db), cache_size), + children_access: CachedDbAccess::new(db, cache_size), + } + } + + pub fn clone_with_new_cache(&self, cache_size: u64) -> Self { + Self::new(Arc::clone(&self.db), self.level, cache_size) + } + + pub fn insert_batch( + &mut self, + batch: &mut WriteBatch, + hash: Hash, + parents: BlockHashes, + ) -> Result<(), StoreError> { + if self.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + + // Insert a new entry for `hash` + self.parents_access + .write(BatchDbWriter::new(batch), hash, parents.clone())?; + + // The new hash has no children yet + self.children_access.write( + BatchDbWriter::new(batch), + hash, + BlockHashes::new(Vec::new()), + )?; + + // Update `children` for each parent + for parent in parents.iter().cloned() { + let mut children = (*self.get_children(parent)?).clone(); + children.push(hash); + self.children_access.write( + BatchDbWriter::new(batch), + parent, + BlockHashes::new(children), + )?; + } + + Ok(()) + } +} + +impl RelationsStoreReader for DbRelationsStore { + fn get_parents(&self, hash: Hash) -> Result { + self.parents_access.read(hash) + } + + fn get_children(&self, hash: Hash) -> Result { + self.children_access.read(hash) + } + + fn has(&self, hash: Hash) -> Result { + if self.parents_access.has(hash)? { + debug_assert!(self.children_access.has(hash)?); + Ok(true) + } else { + Ok(false) + } + } +} + +impl RelationsStore for DbRelationsStore { + /// See `insert_batch` as well + /// TODO: use one function with DbWriter for both this function and insert_batch + fn insert(&mut self, hash: Hash, parents: BlockHashes) -> Result<(), StoreError> { + if self.has(hash)? { + return Err(StoreError::KeyAlreadyExists(hash.to_string())); + } + + // Insert a new entry for `hash` + self.parents_access + .write(DirectDbWriter::new(&self.db), hash, parents.clone())?; + + // The new hash has no children yet + self.children_access.write( + DirectDbWriter::new(&self.db), + hash, + BlockHashes::new(Vec::new()), + )?; + + // Update `children` for each parent + for parent in parents.iter().cloned() { + let mut children = (*self.get_children(parent)?).clone(); + children.push(hash); + self.children_access.write( + DirectDbWriter::new(&self.db), + parent, + BlockHashes::new(children), + )?; + } + + Ok(()) + } +} + +pub struct MemoryRelationsStore { + parents_map: BlockHashMap, + children_map: BlockHashMap, +} + +impl MemoryRelationsStore { + pub fn new() -> Self { + Self { + parents_map: BlockHashMap::new(), + children_map: BlockHashMap::new(), + } + } +} + +impl Default for MemoryRelationsStore { + fn default() -> Self { + Self::new() + } +} + +impl RelationsStoreReader for MemoryRelationsStore { + fn get_parents(&self, hash: Hash) -> Result { + match self.parents_map.get(&hash) { + Some(parents) => Ok(BlockHashes::clone(parents)), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn get_children(&self, hash: Hash) -> Result { + match self.children_map.get(&hash) { + Some(children) => Ok(BlockHashes::clone(children)), + None => Err(StoreError::KeyNotFound(hash.to_string())), + } + } + + fn has(&self, hash: Hash) -> Result { + Ok(self.parents_map.contains_key(&hash)) + } +} + +impl RelationsStore for MemoryRelationsStore { + fn insert(&mut self, hash: Hash, parents: BlockHashes) -> Result<(), StoreError> { + if let Vacant(e) = self.parents_map.entry(hash) { + // Update the new entry for `hash` + e.insert(BlockHashes::clone(&parents)); + + // Update `children` for each parent + for parent in parents.iter().cloned() { + let mut children = (*self.get_children(parent)?).clone(); + children.push(hash); + self.children_map.insert(parent, BlockHashes::new(children)); + } + + // The new hash has no children yet + self.children_map.insert(hash, BlockHashes::new(Vec::new())); + Ok(()) + } else { + Err(StoreError::KeyAlreadyExists(hash.to_string())) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::consensusdb::{ + db::RelationsStoreConfig, + prelude::{FlexiDagStorage, FlexiDagStorageConfig}, + }; + + #[test] + fn test_memory_relations_store() { + test_relations_store(MemoryRelationsStore::new()); + } + + #[test] + fn test_db_relations_store() { + let db_tempdir = tempfile::tempdir().unwrap(); + let rs_conf = RelationsStoreConfig { + block_level: 0, + cache_size: 2, + }; + let config = FlexiDagStorageConfig::new() + .update_parallelism(1) + .update_relations_conf(rs_conf); + + let db = FlexiDagStorage::create_from_path(db_tempdir.path(), config) + .expect("failed to create flexidag storage"); + test_relations_store(db.relations_store); + } + + fn test_relations_store(mut store: T) { + let parents = [ + (1, vec![]), + (2, vec![1]), + (3, vec![1]), + (4, vec![2, 3]), + (5, vec![1, 4]), + ]; + for (i, vec) in parents.iter().cloned() { + store + .insert( + i.into(), + BlockHashes::new(vec.iter().copied().map(Hash::from).collect()), + ) + .unwrap(); + } + + let expected_children = [ + (1, vec![2, 3, 5]), + (2, vec![4]), + (3, vec![4]), + (4, vec![5]), + (5, vec![]), + ]; + for (i, vec) in expected_children { + assert!(store + .get_children(i.into()) + .unwrap() + .iter() + .copied() + .eq(vec.iter().copied().map(Hash::from))); + } + + for (i, vec) in parents { + assert!(store + .get_parents(i.into()) + .unwrap() + .iter() + .copied() + .eq(vec.iter().copied().map(Hash::from))); + } + } +} diff --git a/consensus/src/consensusdb/db.rs b/consensus/src/consensusdb/db.rs new file mode 100644 index 0000000000..331df80277 --- /dev/null +++ b/consensus/src/consensusdb/db.rs @@ -0,0 +1,149 @@ +use super::{ + error::StoreError, + schemadb::{ + DbGhostdagStore, DbHeadersStore, DbReachabilityStore, DbRelationsStore, CHILDREN_CF, + COMPACT_GHOST_DAG_STORE_CF, COMPACT_HEADER_DATA_STORE_CF, GHOST_DAG_STORE_CF, + HEADERS_STORE_CF, PARENTS_CF, REACHABILITY_DATA_CF, + }, +}; +use starcoin_config::RocksdbConfig; +pub(crate) use starcoin_storage::db_storage::DBStorage; +use std::{path::Path, sync::Arc}; + +#[derive(Clone)] +pub struct FlexiDagStorage { + pub ghost_dag_store: DbGhostdagStore, + pub header_store: DbHeadersStore, + pub reachability_store: DbReachabilityStore, + pub relations_store: DbRelationsStore, +} + +#[derive(Clone, Default)] +pub struct GhostDagStoreConfig { + pub block_level: u8, + pub cache_size: u64, +} + +#[derive(Clone, Default)] +pub struct HeaderStoreConfig { + pub cache_size: u64, +} + +#[derive(Clone, Default)] +pub struct ReachabilityStoreConfig { + pub cache_size: u64, +} + +#[derive(Clone, Default)] +pub struct RelationsStoreConfig { + pub block_level: u8, + pub cache_size: u64, +} + +#[derive(Clone, Default)] +pub struct FlexiDagStorageConfig { + pub parallelism: u64, + pub gds_conf: GhostDagStoreConfig, + pub hs_conf: HeaderStoreConfig, + pub rbs_conf: ReachabilityStoreConfig, + pub rs_conf: RelationsStoreConfig, +} + +impl FlexiDagStorageConfig { + pub fn new() -> Self { + FlexiDagStorageConfig::default() + } + + pub fn create_with_params(parallelism: u64, block_level: u8, cache_size: u64) -> Self { + Self { + parallelism, + gds_conf: GhostDagStoreConfig { + block_level, + cache_size, + }, + hs_conf: HeaderStoreConfig { cache_size }, + rbs_conf: ReachabilityStoreConfig { cache_size }, + rs_conf: RelationsStoreConfig { + block_level, + cache_size, + }, + } + } + + pub fn update_parallelism(mut self, parallelism: u64) -> Self { + self.parallelism = parallelism; + self + } + + pub fn update_ghost_dag_conf(mut self, gds_conf: GhostDagStoreConfig) -> Self { + self.gds_conf = gds_conf; + self + } + + pub fn update_headers_conf(mut self, hs_conf: HeaderStoreConfig) -> Self { + self.hs_conf = hs_conf; + self + } + + pub fn update_reachability_conf(mut self, rbs_conf: ReachabilityStoreConfig) -> Self { + self.rbs_conf = rbs_conf; + self + } + + pub fn update_relations_conf(mut self, rs_conf: RelationsStoreConfig) -> Self { + self.rs_conf = rs_conf; + self + } +} + +impl FlexiDagStorage { + /// Creates or loads an existing storage from the provided directory path. + pub fn create_from_path>( + db_path: P, + config: FlexiDagStorageConfig, + ) -> Result { + let rocksdb_config = RocksdbConfig { + parallelism: config.parallelism, + ..Default::default() + }; + + let db = Arc::new( + DBStorage::open_with_cfs( + db_path, + vec![ + // consensus headers + HEADERS_STORE_CF, + COMPACT_HEADER_DATA_STORE_CF, + // consensus relations + PARENTS_CF, + CHILDREN_CF, + // consensus reachability + REACHABILITY_DATA_CF, + // consensus ghostdag + GHOST_DAG_STORE_CF, + COMPACT_GHOST_DAG_STORE_CF, + ], + false, + rocksdb_config, + None, + ) + .map_err(|e| StoreError::DBIoError(e.to_string()))?, + ); + + Ok(Self { + ghost_dag_store: DbGhostdagStore::new( + db.clone(), + config.gds_conf.block_level, + config.gds_conf.cache_size, + ), + + header_store: DbHeadersStore::new(db.clone(), config.hs_conf.cache_size), + reachability_store: DbReachabilityStore::new(db.clone(), config.rbs_conf.cache_size), + relations_store: DbRelationsStore::new( + db, + config.rs_conf.block_level, + config.rs_conf.cache_size, + ), + }) + } +} diff --git a/consensus/src/consensusdb/error.rs b/consensus/src/consensusdb/error.rs new file mode 100644 index 0000000000..ff2c199c93 --- /dev/null +++ b/consensus/src/consensusdb/error.rs @@ -0,0 +1,58 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum StoreError { + #[error("key {0} not found in store")] + KeyNotFound(String), + + #[error("key {0} already exists in store")] + KeyAlreadyExists(String), + + #[error("column family {0} not exist in db")] + CFNotExist(String), + + #[error("IO error {0}")] + DBIoError(String), + + #[error("rocksdb error {0}")] + DbError(#[from] rocksdb::Error), + + #[error("encode error {0}")] + EncodeError(String), + + #[error("decode error {0}")] + DecodeError(String), + + #[error("ghostdag {0} duplicate blocks")] + DAGDupBlocksError(String), +} + +pub type StoreResult = std::result::Result; + +pub trait StoreResultExtensions { + fn unwrap_option(self) -> Option; +} + +impl StoreResultExtensions for StoreResult { + fn unwrap_option(self) -> Option { + match self { + Ok(value) => Some(value), + Err(StoreError::KeyNotFound(_)) => None, + Err(err) => panic!("Unexpected store error: {err:?}"), + } + } +} + +pub trait StoreResultEmptyTuple { + fn unwrap_and_ignore_key_already_exists(self); +} + +impl StoreResultEmptyTuple for StoreResult<()> { + fn unwrap_and_ignore_key_already_exists(self) { + match self { + Ok(_) => (), + Err(StoreError::KeyAlreadyExists(_)) => (), + Err(err) => panic!("Unexpected store error: {err:?}"), + } + } +} diff --git a/consensus/src/consensusdb/item.rs b/consensus/src/consensusdb/item.rs new file mode 100644 index 0000000000..0d27b9c347 --- /dev/null +++ b/consensus/src/consensusdb/item.rs @@ -0,0 +1,81 @@ +use super::prelude::DbWriter; +use super::schema::{KeyCodec, Schema, ValueCodec}; +use super::{db::DBStorage, error::StoreError}; +use parking_lot::RwLock; +use starcoin_storage::storage::RawDBStorage; +use std::sync::Arc; + +/// A cached DB item with concurrency support +#[derive(Clone)] +pub struct CachedDbItem { + db: Arc, + key: S::Key, + cached_item: Arc>>, +} + +impl CachedDbItem { + pub fn new(db: Arc, key: S::Key) -> Self { + Self { + db, + key, + cached_item: Arc::new(RwLock::new(None)), + } + } + + pub fn read(&self) -> Result { + if let Some(item) = self.cached_item.read().clone() { + return Ok(item); + } + if let Some(slice) = self + .db + .raw_get_pinned_cf(S::COLUMN_FAMILY, &self.key.encode_key()?) + .map_err(|_| StoreError::CFNotExist(S::COLUMN_FAMILY.to_string()))? + { + let item = S::Value::decode_value(&slice)?; + *self.cached_item.write() = Some(item.clone()); + Ok(item) + } else { + Err(StoreError::KeyNotFound( + String::from_utf8(self.key.encode_key()?) + .unwrap_or(("unrecoverable key string").to_string()), + )) + } + } + + pub fn write(&mut self, mut writer: impl DbWriter, item: &S::Value) -> Result<(), StoreError> { + *self.cached_item.write() = Some(item.clone()); + writer.put::(&self.key, item)?; + Ok(()) + } + + pub fn remove(&mut self, mut writer: impl DbWriter) -> Result<(), StoreError> +where { + *self.cached_item.write() = None; + writer.delete::(&self.key)?; + Ok(()) + } + + pub fn update(&mut self, mut writer: impl DbWriter, op: F) -> Result + where + F: Fn(S::Value) -> S::Value, + { + let mut guard = self.cached_item.write(); + let mut item = if let Some(item) = guard.take() { + item + } else if let Some(slice) = self + .db + .raw_get_pinned_cf(S::COLUMN_FAMILY, &self.key.encode_key()?) + .map_err(|_| StoreError::CFNotExist(S::COLUMN_FAMILY.to_string()))? + { + let item = S::Value::decode_value(&slice)?; + item + } else { + return Err(StoreError::KeyNotFound("".to_string())); + }; + + item = op(item); // Apply the update op + *guard = Some(item.clone()); + writer.put::(&self.key, &item)?; + Ok(item) + } +} diff --git a/consensus/src/consensusdb/mod.rs b/consensus/src/consensusdb/mod.rs new file mode 100644 index 0000000000..5aaa7c6ef2 --- /dev/null +++ b/consensus/src/consensusdb/mod.rs @@ -0,0 +1,31 @@ +mod access; +mod cache; +mod consensus_ghostdag; +mod consensus_header; +mod consensus_reachability; +pub mod consensus_relations; +mod db; +mod error; +mod item; +pub mod schema; +mod writer; + +pub mod prelude { + use super::{db, error}; + + pub use super::{ + access::CachedDbAccess, + cache::DagCache, + item::CachedDbItem, + writer::{BatchDbWriter, DbWriter, DirectDbWriter}, + }; + pub use db::{FlexiDagStorage, FlexiDagStorageConfig}; + pub use error::{StoreError, StoreResult, StoreResultEmptyTuple, StoreResultExtensions}; +} + +pub mod schemadb { + pub use super::{ + consensus_ghostdag::*, consensus_header::*, consensus_reachability::*, + consensus_relations::*, + }; +} diff --git a/consensus/src/consensusdb/schema.rs b/consensus/src/consensusdb/schema.rs new file mode 100644 index 0000000000..ad1bbc072f --- /dev/null +++ b/consensus/src/consensusdb/schema.rs @@ -0,0 +1,40 @@ +use super::error::StoreError; +use core::hash::Hash; +use std::fmt::Debug; +use std::result::Result; + +pub trait KeyCodec: Clone + Sized + Debug + Send + Sync { + /// Converts `self` to bytes to be stored in DB. + fn encode_key(&self) -> Result, StoreError>; + /// Converts bytes fetched from DB to `Self`. + fn decode_key(data: &[u8]) -> Result; +} + +pub trait ValueCodec: Clone + Sized + Debug + Send + Sync { + /// Converts `self` to bytes to be stored in DB. + fn encode_value(&self) -> Result, StoreError>; + /// Converts bytes fetched from DB to `Self`. + fn decode_value(data: &[u8]) -> Result; +} + +pub trait Schema: Debug + Send + Sync + 'static { + const COLUMN_FAMILY: &'static str; + + type Key: KeyCodec + Hash + Eq + Default; + type Value: ValueCodec + Default + Clone; +} + +#[macro_export] +macro_rules! define_schema { + ($schema_type: ident, $key_type: ty, $value_type: ty, $cf_name: expr) => { + #[derive(Clone, Debug)] + pub(crate) struct $schema_type; + + impl $crate::schema::Schema for $schema_type { + type Key = $key_type; + type Value = $value_type; + + const COLUMN_FAMILY: &'static str = $cf_name; + } + }; +} diff --git a/consensus/src/consensusdb/writer.rs b/consensus/src/consensusdb/writer.rs new file mode 100644 index 0000000000..717d7d7e1c --- /dev/null +++ b/consensus/src/consensusdb/writer.rs @@ -0,0 +1,75 @@ +use rocksdb::WriteBatch; +use starcoin_storage::storage::InnerStore; + +use super::schema::{KeyCodec, Schema, ValueCodec}; +use super::{db::DBStorage, error::StoreError}; + +/// Abstraction over direct/batched DB writing +pub trait DbWriter { + fn put(&mut self, key: &S::Key, value: &S::Value) -> Result<(), StoreError>; + fn delete(&mut self, key: &S::Key) -> Result<(), StoreError>; +} + +pub struct DirectDbWriter<'a> { + db: &'a DBStorage, +} + +impl<'a> DirectDbWriter<'a> { + pub fn new(db: &'a DBStorage) -> Self { + Self { db } + } +} + +impl DbWriter for DirectDbWriter<'_> { + fn put(&mut self, key: &S::Key, value: &S::Value) -> Result<(), StoreError> { + let bin_key = key.encode_key()?; + let bin_data = value.encode_value()?; + self.db + .put(S::COLUMN_FAMILY, bin_key, bin_data) + .map_err(|e| StoreError::DBIoError(e.to_string())) + } + + fn delete(&mut self, key: &S::Key) -> Result<(), StoreError> { + let key = key.encode_key()?; + self.db + .remove(S::COLUMN_FAMILY, key) + .map_err(|e| StoreError::DBIoError(e.to_string())) + } +} + +pub struct BatchDbWriter<'a> { + batch: &'a mut WriteBatch, +} + +impl<'a> BatchDbWriter<'a> { + pub fn new(batch: &'a mut WriteBatch) -> Self { + Self { batch } + } +} + +impl DbWriter for BatchDbWriter<'_> { + fn put(&mut self, key: &S::Key, value: &S::Value) -> Result<(), StoreError> { + let key = key.encode_key()?; + let value = value.encode_value()?; + self.batch.put(key, value); + Ok(()) + } + + fn delete(&mut self, key: &S::Key) -> Result<(), StoreError> { + let key = key.encode_key()?; + self.batch.delete(key); + Ok(()) + } +} + +impl DbWriter for &mut T { + #[inline] + fn put(&mut self, key: &S::Key, value: &S::Value) -> Result<(), StoreError> { + (*self).put::(key, value) + } + + #[inline] + fn delete(&mut self, key: &S::Key) -> Result<(), StoreError> { + (*self).delete::(key) + } +} diff --git a/consensus/src/dag/blockdag.rs b/consensus/src/dag/blockdag.rs new file mode 100644 index 0000000000..e23c8d1d00 --- /dev/null +++ b/consensus/src/dag/blockdag.rs @@ -0,0 +1,260 @@ +use super::ghostdag::protocol::{ColoringOutput, GhostdagManager}; +use super::reachability::{inquirer, reachability_service::MTReachabilityService}; +use super::types::ghostdata::GhostdagData; +use crate::consensusdb::prelude::StoreError; +use crate::consensusdb::schemadb::GhostdagStoreReader; +use crate::consensusdb::{ + prelude::FlexiDagStorage, + schemadb::{ + DbGhostdagStore, DbHeadersStore, DbReachabilityStore, DbRelationsStore, GhostdagStore, + HeaderStore, ReachabilityStoreReader, RelationsStore, RelationsStoreReader, + }, +}; +use anyhow::{anyhow, bail, Ok}; +use parking_lot::RwLock; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::{ + blockhash::{BlockHashes, KType, ORIGIN}, + header::{ConsensusHeader, DagHeader}, +}; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; + +pub type DbGhostdagManager = GhostdagManager< + DbGhostdagStore, + DbRelationsStore, + MTReachabilityService, + DbHeadersStore, +>; + +#[derive(Clone)] +pub struct BlockDAG { + genesis_hash: Hash, + ghostdag_manager: DbGhostdagManager, + relations_store: DbRelationsStore, + reachability_store: DbReachabilityStore, + ghostdag_store: DbGhostdagStore, + header_store: DbHeadersStore, + /// orphan blocks, parent hash -> orphan block + missing_blocks: HashMap>, +} + +impl BlockDAG { + pub fn new(genesis_hash: Hash, k: KType, db: FlexiDagStorage) -> Self { + let ghostdag_store = db.ghost_dag_store.clone(); + let header_store = db.header_store.clone(); + let relations_store = db.relations_store.clone(); + let mut reachability_store = db.reachability_store; + inquirer::init(&mut reachability_store).unwrap(); + let reachability_service = + MTReachabilityService::new(Arc::new(RwLock::new(reachability_store.clone()))); + let ghostdag_manager = DbGhostdagManager::new( + genesis_hash, + k, + ghostdag_store.clone(), + relations_store.clone(), + header_store.clone(), + reachability_service, + ); + + let mut dag = Self { + genesis_hash, + ghostdag_manager, + relations_store, + reachability_store, + ghostdag_store, + header_store, + missing_blocks: HashMap::new(), + }; + dag + } + + pub fn clear_missing_block(&mut self) { + self.missing_blocks.clear(); + } + + pub fn init_with_genesis(&mut self, genesis: DagHeader) -> anyhow::Result<()> { + if self.relations_store.has(Hash::new(ORIGIN))? { + return Err(anyhow!("Already init with genesis")); + }; + self.relations_store + .insert(Hash::new(ORIGIN), BlockHashes::new(vec![])) + .unwrap(); + let _ = self.addToDag(genesis); + Ok(()) + } + + pub fn addToDag(&mut self, header: DagHeader) -> anyhow::Result { + //TODO:check genesis + // Generate ghostdag data + let parents_hash = header.parents_hash(); + let ghostdag_data = if header.hash() != self.genesis_hash { + self.ghostdag_manager.ghostdag(parents_hash) + } else { + self.ghostdag_manager.genesis_ghostdag_data() + }; + // Store ghostdata + self.ghostdag_store + .insert(header.hash(), Arc::new(ghostdag_data.clone())) + .unwrap(); + + // Update reachability store + let mut reachability_store = self.reachability_store.clone(); + let mut merge_set = ghostdag_data + .unordered_mergeset_without_selected_parent() + .filter(|hash| self.reachability_store.has(*hash).unwrap()); + + inquirer::add_block( + &mut reachability_store, + header.hash(), + ghostdag_data.selected_parent, + &mut merge_set, + )?; + + // store relations + self.relations_store + .insert(header.hash(), BlockHashes::new(parents_hash.to_vec()))?; + // Store header store + let _ = self + .header_store + .insert(header.hash(), Arc::new(header.to_owned()), 0)?; + return Ok(ghostdag_data.clone()); + } + + fn is_in_dag(&self, _hash: Hash) -> anyhow::Result { + return Ok(true); + } + pub fn verify_header(&self, _header: &DagHeader) -> anyhow::Result<()> { + //TODO: implemented it + Ok(()) + } + + pub fn connect_block(&mut self, header: DagHeader) -> anyhow::Result<()> { + let _ = self.verify_header(&header)?; + let is_orphan_block = self.update_orphans(&header)?; + if is_orphan_block { + return Ok(()); + } + self.addToDag(header.clone()); + self.check_missing_block(header)?; + Ok(()) + } + + pub fn check_missing_block(&mut self, header: DagHeader) -> anyhow::Result<()> { + if let Some(orphans) = self.missing_blocks.remove(&header.hash()) { + for orphan in orphans.iter() { + let is_orphan = self.is_orphan(&orphan)?; + if !is_orphan { + self.addToDag(header.clone()); + } + } + } + Ok(()) + } + fn is_orphan(&self, header: &DagHeader) -> anyhow::Result { + for parent in header.parents_hash() { + if !self.is_in_dag(parent.to_owned())? { + return Ok(false); + } + } + return Ok(true); + } + pub fn get_ghostdag_data(&self, hash: Hash) -> anyhow::Result> { + let ghostdata = self.ghostdag_store.get_data(hash)?; + return Ok(ghostdata); + } + + fn update_orphans(&mut self, block_header: &DagHeader) -> anyhow::Result { + let mut is_orphan = false; + for parent in block_header.parents_hash() { + if self.is_in_dag(parent.to_owned())? { + continue; + } + if !self + .missing_blocks + .entry(parent.to_owned()) + .or_insert_with(HashSet::new) + .insert(block_header.to_owned()) + { + return Err(anyhow::anyhow!("Block already processed as a orphan")); + } + is_orphan = true; + } + Ok(is_orphan) + } + + pub fn get_block_header(&self, hash: Hash) -> anyhow::Result { + match self.header_store.get_header(hash) { + anyhow::Result::Ok(header) => anyhow::Result::Ok(header), + Err(error) => { + println!("failed to get header by hash: {}", error.to_string()); + bail!("failed to get header by hash: {}", error.to_string()); + } + } + } + + pub fn get_parents(&self, hash: Hash) -> anyhow::Result> { + match self.relations_store.get_parents(hash) { + anyhow::Result::Ok(parents) => anyhow::Result::Ok((*parents).clone()), + Err(error) => { + println!("failed to get parents by hash: {}", error.to_string()); + bail!("failed to get parents by hash: {}", error.to_string()); + } + } + } + + pub fn get_children(&self, hash: Hash) -> anyhow::Result> { + match self.relations_store.get_children(hash) { + anyhow::Result::Ok(children) => anyhow::Result::Ok((*children).clone()), + Err(error) => { + println!("failed to get parents by hash: {}", error.to_string()); + bail!("failed to get parents by hash: {}", error.to_string()); + } + } + } + + // for testing + pub fn push_parent_children( + &mut self, + child: Hash, + parents: Arc>, + ) -> Result<(), StoreError> { + self.relations_store.insert(child, parents) + } + + pub fn get_genesis_hash(&self) -> Hash { + self.genesis_hash + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::consensusdb::prelude::{FlexiDagStorage, FlexiDagStorageConfig}; + use starcoin_types::block::BlockHeader; + use std::{env, fs}; + + #[test] + fn base_test() { + let genesis = DagHeader::new_genesis(BlockHeader::random()); + let genesis_hash = genesis.hash(); + let k = 16; + let db_path = env::temp_dir().join("smolstc"); + println!("db path:{}", db_path.to_string_lossy()); + if db_path + .as_path() + .try_exists() + .unwrap_or_else(|_| panic!("Failed to check {db_path:?}")) + { + fs::remove_dir_all(db_path.as_path()).expect("Failed to delete temporary directory"); + } + let config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); + let db = FlexiDagStorage::create_from_path(db_path, config) + .expect("Failed to create flexidag storage"); + let mut dag = BlockDAG::new(genesis_hash, k, db); + dag.init_with_genesis(genesis); + let block = DagHeader::new(BlockHeader::random(), vec![genesis_hash]); + dag.addToDag(block); + } +} diff --git a/consensus/src/dag/ghostdag/mergeset.rs b/consensus/src/dag/ghostdag/mergeset.rs new file mode 100644 index 0000000000..79aefe2db7 --- /dev/null +++ b/consensus/src/dag/ghostdag/mergeset.rs @@ -0,0 +1,71 @@ +use super::protocol::GhostdagManager; +use crate::consensusdb::schemadb::{GhostdagStoreReader, HeaderStoreReader, RelationsStoreReader}; +use crate::dag::reachability::reachability_service::ReachabilityService; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::BlockHashSet; +use std::collections::VecDeque; + +impl< + T: GhostdagStoreReader, + S: RelationsStoreReader, + U: ReachabilityService, + V: HeaderStoreReader, + > GhostdagManager +{ + pub fn ordered_mergeset_without_selected_parent( + &self, + selected_parent: Hash, + parents: &[Hash], + ) -> Vec { + self.sort_blocks(self.unordered_mergeset_without_selected_parent(selected_parent, parents)) + } + + pub fn unordered_mergeset_without_selected_parent( + &self, + selected_parent: Hash, + parents: &[Hash], + ) -> BlockHashSet { + let mut queue: VecDeque<_> = parents + .iter() + .copied() + .filter(|p| p != &selected_parent) + .collect(); + let mut mergeset: BlockHashSet = queue.iter().copied().collect(); + let mut selected_parent_past = BlockHashSet::new(); + + while let Some(current) = queue.pop_front() { + let current_parents = self + .relations_store + .get_parents(current) + .unwrap_or_else(|err| { + println!("WUT"); + panic!("{err:?}"); + }); + + // For each parent of the current block we check whether it is in the past of the selected parent. If not, + // we add it to the resulting merge-set and queue it for further processing. + for parent in current_parents.iter() { + if mergeset.contains(parent) { + continue; + } + + if selected_parent_past.contains(parent) { + continue; + } + + if self + .reachability_service + .is_dag_ancestor_of(*parent, selected_parent) + { + selected_parent_past.insert(*parent); + continue; + } + + mergeset.insert(*parent); + queue.push_back(*parent); + } + } + + mergeset + } +} diff --git a/consensus/src/dag/ghostdag/mod.rs b/consensus/src/dag/ghostdag/mod.rs new file mode 100644 index 0000000000..51a2c8fc82 --- /dev/null +++ b/consensus/src/dag/ghostdag/mod.rs @@ -0,0 +1,4 @@ +pub mod mergeset; +pub mod protocol; + +mod util; diff --git a/consensus/src/dag/ghostdag/protocol.rs b/consensus/src/dag/ghostdag/protocol.rs new file mode 100644 index 0000000000..9afc86d3bd --- /dev/null +++ b/consensus/src/dag/ghostdag/protocol.rs @@ -0,0 +1,338 @@ +use super::util::Refs; +use crate::consensusdb::schemadb::{GhostdagStoreReader, HeaderStoreReader, RelationsStoreReader}; +use crate::dag::reachability::reachability_service::ReachabilityService; +use crate::dag::types::{ghostdata::GhostdagData, ordering::*}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{ + self, BlockHashExtensions, BlockHashMap, BlockHashes, BlueWorkType, HashKTypeMap, KType, +}; +use std::sync::Arc; +// For GhostdagStoreReader-related functions, use GhostDagDataWrapper instead. +// ascending_mergeset_without_selected_parent +// descending_mergeset_without_selected_parent +// consensus_ordered_mergeset +// consensus_ordered_mergeset_without_selected_parent +//use dag_database::consensus::GhostDagDataWrapper; + +#[derive(Clone)] +pub struct GhostdagManager< + T: GhostdagStoreReader, + S: RelationsStoreReader, + U: ReachabilityService, + V: HeaderStoreReader, +> { + genesis_hash: Hash, + pub(super) k: KType, + pub(super) ghostdag_store: T, + pub(super) relations_store: S, + pub(super) headers_store: V, + pub(super) reachability_service: U, +} + +impl< + T: GhostdagStoreReader, + S: RelationsStoreReader, + U: ReachabilityService, + V: HeaderStoreReader, + > GhostdagManager +{ + pub fn new( + genesis_hash: Hash, + k: KType, + ghostdag_store: T, + relations_store: S, + headers_store: V, + reachability_service: U, + ) -> Self { + Self { + genesis_hash, + k, + ghostdag_store, + relations_store, + reachability_service, + headers_store, + } + } + + pub fn genesis_ghostdag_data(&self) -> GhostdagData { + GhostdagData::new( + 0, + Default::default(), // TODO: take blue score and work from actual genesis + Hash::new(blockhash::ORIGIN), + BlockHashes::new(Vec::new()), + BlockHashes::new(Vec::new()), + HashKTypeMap::new(BlockHashMap::new()), + ) + } + + pub fn origin_ghostdag_data(&self) -> Arc { + Arc::new(GhostdagData::new( + 0, + Default::default(), + 0.into(), + BlockHashes::new(Vec::new()), + BlockHashes::new(Vec::new()), + HashKTypeMap::new(BlockHashMap::new()), + )) + } + + pub fn find_selected_parent(&self, parents: impl IntoIterator) -> Hash { + parents + .into_iter() + .map(|parent| SortableBlock { + hash: parent, + blue_work: self.ghostdag_store.get_blue_work(parent).unwrap(), + }) + .max() + .unwrap() + .hash + } + + /// Runs the GHOSTDAG protocol and calculates the block GhostdagData by the given parents. + /// The function calculates mergeset blues by iterating over the blocks in + /// the anticone of the new block selected parent (which is the parent with the + /// highest blue work) and adds any block to the blue set if by adding + /// it these conditions will not be violated: + /// + /// 1) |anticone-of-candidate-block ∩ blue-set-of-new-block| ≤ K + /// + /// 2) For every blue block in blue-set-of-new-block: + /// |(anticone-of-blue-block ∩ blue-set-new-block) ∪ {candidate-block}| ≤ K. + /// We validate this condition by maintaining a map blues_anticone_sizes for + /// each block which holds all the blue anticone sizes that were affected by + /// the new added blue blocks. + /// So to find out what is |anticone-of-blue ∩ blue-set-of-new-block| we just iterate in + /// the selected parent chain of the new block until we find an existing entry in + /// blues_anticone_sizes. + /// + /// For further details see the article https://eprint.iacr.org/2018/104.pdf + pub fn ghostdag(&self, parents: &[Hash]) -> GhostdagData { + assert!( + !parents.is_empty(), + "genesis must be added via a call to init" + ); + + // Run the GHOSTDAG parent selection algorithm + let selected_parent = self.find_selected_parent(&mut parents.iter().copied()); + // Initialize new GHOSTDAG block data with the selected parent + let mut new_block_data = GhostdagData::new_with_selected_parent(selected_parent, self.k); + // Get the mergeset in consensus-agreed topological order (topological here means forward in time from blocks to children) + let ordered_mergeset = + self.ordered_mergeset_without_selected_parent(selected_parent, parents); + + for blue_candidate in ordered_mergeset.iter().cloned() { + let coloring = self.check_blue_candidate(&new_block_data, blue_candidate); + + if let ColoringOutput::Blue(blue_anticone_size, blues_anticone_sizes) = coloring { + // No k-cluster violation found, we can now set the candidate block as blue + new_block_data.add_blue(blue_candidate, blue_anticone_size, &blues_anticone_sizes); + } else { + new_block_data.add_red(blue_candidate); + } + } + + let blue_score = self + .ghostdag_store + .get_blue_score(selected_parent) + .unwrap() + .checked_add(new_block_data.mergeset_blues.len() as u64) + .unwrap(); + + let added_blue_work: BlueWorkType = new_block_data + .mergeset_blues + .iter() + .cloned() + .map(|hash| { + if hash.is_origin() { + 0u128 + } else { + //TODO: implement caculate pow work + let _difficulty = self.headers_store.get_difficulty(hash).unwrap(); + 1024u128 + } + }) + .sum(); + + let blue_work = self + .ghostdag_store + .get_blue_work(selected_parent) + .unwrap() + .checked_add(added_blue_work) + .unwrap(); + new_block_data.finalize_score_and_work(blue_score, blue_work); + + new_block_data + } + + fn check_blue_candidate_with_chain_block( + &self, + new_block_data: &GhostdagData, + chain_block: &ChainBlock, + blue_candidate: Hash, + candidate_blues_anticone_sizes: &mut BlockHashMap, + candidate_blue_anticone_size: &mut KType, + ) -> ColoringState { + // If blue_candidate is in the future of chain_block, it means + // that all remaining blues are in the past of chain_block and thus + // in the past of blue_candidate. In this case we know for sure that + // the anticone of blue_candidate will not exceed K, and we can mark + // it as blue. + // + // The new block is always in the future of blue_candidate, so there's + // no point in checking it. + + // We check if chain_block is not the new block by checking if it has a hash. + if let Some(hash) = chain_block.hash { + if self + .reachability_service + .is_dag_ancestor_of(hash, blue_candidate) + { + return ColoringState::Blue; + } + } + + for &block in chain_block.data.mergeset_blues.iter() { + // Skip blocks that exist in the past of blue_candidate. + if self + .reachability_service + .is_dag_ancestor_of(block, blue_candidate) + { + continue; + } + + candidate_blues_anticone_sizes + .insert(block, self.blue_anticone_size(block, new_block_data)); + + *candidate_blue_anticone_size = (*candidate_blue_anticone_size).checked_add(1).unwrap(); + if *candidate_blue_anticone_size > self.k { + // k-cluster violation: The candidate's blue anticone exceeded k + return ColoringState::Red; + } + + if *candidate_blues_anticone_sizes.get(&block).unwrap() == self.k { + // k-cluster violation: A block in candidate's blue anticone already + // has k blue blocks in its own anticone + return ColoringState::Red; + } + + // This is a sanity check that validates that a blue + // block's blue anticone is not already larger than K. + assert!( + *candidate_blues_anticone_sizes.get(&block).unwrap() <= self.k, + "found blue anticone larger than K" + ); + } + + ColoringState::Pending + } + + /// Returns the blue anticone size of `block` from the worldview of `context`. + /// Expects `block` to be in the blue set of `context` + fn blue_anticone_size(&self, block: Hash, context: &GhostdagData) -> KType { + let mut current_blues_anticone_sizes = HashKTypeMap::clone(&context.blues_anticone_sizes); + let mut current_selected_parent = context.selected_parent; + loop { + if let Some(size) = current_blues_anticone_sizes.get(&block) { + return *size; + } + + if current_selected_parent == self.genesis_hash + || current_selected_parent == Hash::new(blockhash::ORIGIN) + { + panic!("block {block} is not in blue set of the given context"); + } + + current_blues_anticone_sizes = self + .ghostdag_store + .get_blues_anticone_sizes(current_selected_parent) + .unwrap(); + current_selected_parent = self + .ghostdag_store + .get_selected_parent(current_selected_parent) + .unwrap(); + } + } + + pub fn check_blue_candidate( + &self, + new_block_data: &GhostdagData, + blue_candidate: Hash, + ) -> ColoringOutput { + // The maximum length of new_block_data.mergeset_blues can be K+1 because + // it contains the selected parent. + if new_block_data.mergeset_blues.len() as KType == self.k.checked_add(1).unwrap() { + return ColoringOutput::Red; + } + + let mut candidate_blues_anticone_sizes: BlockHashMap = + BlockHashMap::with_capacity(self.k as usize); + // Iterate over all blocks in the blue past of the new block that are not in the past + // of blue_candidate, and check for each one of them if blue_candidate potentially + // enlarges their blue anticone to be over K, or that they enlarge the blue anticone + // of blue_candidate to be over K. + let mut chain_block = ChainBlock { + hash: None, + data: new_block_data.into(), + }; + let mut candidate_blue_anticone_size: KType = 0; + + loop { + let state = self.check_blue_candidate_with_chain_block( + new_block_data, + &chain_block, + blue_candidate, + &mut candidate_blues_anticone_sizes, + &mut candidate_blue_anticone_size, + ); + + match state { + ColoringState::Blue => { + return ColoringOutput::Blue( + candidate_blue_anticone_size, + candidate_blues_anticone_sizes, + ) + } + ColoringState::Red => return ColoringOutput::Red, + ColoringState::Pending => (), // continue looping + } + + chain_block = ChainBlock { + hash: Some(chain_block.data.selected_parent), + data: self + .ghostdag_store + .get_data(chain_block.data.selected_parent) + .unwrap() + .into(), + } + } + } + + pub fn sort_blocks(&self, blocks: impl IntoIterator) -> Vec { + let mut sorted_blocks: Vec = blocks.into_iter().collect(); + sorted_blocks.sort_by_cached_key(|block| SortableBlock { + hash: *block, + blue_work: self.ghostdag_store.get_blue_work(*block).unwrap(), + }); + sorted_blocks + } +} + +/// Chain block with attached ghostdag data +struct ChainBlock<'a> { + hash: Option, // if set to `None`, signals being the new block + data: Refs<'a, GhostdagData>, +} + +/// Represents the intermediate GHOSTDAG coloring state for the current candidate +enum ColoringState { + Blue, + Red, + Pending, +} + +#[derive(Debug)] +/// Represents the final output of GHOSTDAG coloring for the current candidate +pub enum ColoringOutput { + Blue(KType, BlockHashMap), // (blue anticone size, map of blue anticone sizes for each affected blue) + Red, +} diff --git a/consensus/src/dag/ghostdag/util.rs b/consensus/src/dag/ghostdag/util.rs new file mode 100644 index 0000000000..68eb4b9b31 --- /dev/null +++ b/consensus/src/dag/ghostdag/util.rs @@ -0,0 +1,57 @@ +use std::{ops::Deref, rc::Rc, sync::Arc}; +/// Enum used to represent a concrete varying pointer type which only needs to be accessed by ref. +/// We avoid adding a `Val(T)` variant in order to keep the size of the enum minimal +pub enum Refs<'a, T> { + Ref(&'a T), + Arc(Arc), + Rc(Rc), + Box(Box), +} + +impl AsRef for Refs<'_, T> { + fn as_ref(&self) -> &T { + match self { + Refs::Ref(r) => r, + Refs::Arc(a) => a, + Refs::Rc(r) => r, + Refs::Box(b) => b, + } + } +} + +impl Deref for Refs<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + Refs::Ref(r) => r, + Refs::Arc(a) => a, + Refs::Rc(r) => r, + Refs::Box(b) => b, + } + } +} + +impl<'a, T> From<&'a T> for Refs<'a, T> { + fn from(r: &'a T) -> Self { + Self::Ref(r) + } +} + +impl From> for Refs<'_, T> { + fn from(a: Arc) -> Self { + Self::Arc(a) + } +} + +impl From> for Refs<'_, T> { + fn from(r: Rc) -> Self { + Self::Rc(r) + } +} + +impl From> for Refs<'_, T> { + fn from(b: Box) -> Self { + Self::Box(b) + } +} diff --git a/consensus/src/dag/mod.rs b/consensus/src/dag/mod.rs new file mode 100644 index 0000000000..9485bd456a --- /dev/null +++ b/consensus/src/dag/mod.rs @@ -0,0 +1,4 @@ +pub mod blockdag; +pub mod ghostdag; +mod reachability; +pub mod types; diff --git a/consensus/src/dag/reachability/extensions.rs b/consensus/src/dag/reachability/extensions.rs new file mode 100644 index 0000000000..9ea769fb9a --- /dev/null +++ b/consensus/src/dag/reachability/extensions.rs @@ -0,0 +1,50 @@ +use crate::consensusdb::{prelude::StoreResult, schemadb::ReachabilityStoreReader}; +use crate::dag::types::interval::Interval; +use starcoin_crypto::hash::HashValue as Hash; + +pub(super) trait ReachabilityStoreIntervalExtensions { + fn interval_children_capacity(&self, block: Hash) -> StoreResult; + fn interval_remaining_before(&self, block: Hash) -> StoreResult; + fn interval_remaining_after(&self, block: Hash) -> StoreResult; +} + +impl ReachabilityStoreIntervalExtensions for T { + /// Returns the reachability allocation capacity for children of `block` + fn interval_children_capacity(&self, block: Hash) -> StoreResult { + // The interval of a block should *strictly* contain the intervals of its + // tree children, hence we subtract 1 from the end of the range. + Ok(self.get_interval(block)?.decrease_end(1)) + } + + /// Returns the available interval to allocate for tree children, taken from the + /// beginning of children allocation capacity + fn interval_remaining_before(&self, block: Hash) -> StoreResult { + let alloc_capacity = self.interval_children_capacity(block)?; + match self.get_children(block)?.first() { + Some(first_child) => { + let first_alloc = self.get_interval(*first_child)?; + Ok(Interval::new( + alloc_capacity.start, + first_alloc.start.checked_sub(1).unwrap(), + )) + } + None => Ok(alloc_capacity), + } + } + + /// Returns the available interval to allocate for tree children, taken from the + /// end of children allocation capacity + fn interval_remaining_after(&self, block: Hash) -> StoreResult { + let alloc_capacity = self.interval_children_capacity(block)?; + match self.get_children(block)?.last() { + Some(last_child) => { + let last_alloc = self.get_interval(*last_child)?; + Ok(Interval::new( + last_alloc.end.checked_add(1).unwrap(), + alloc_capacity.end, + )) + } + None => Ok(alloc_capacity), + } + } +} diff --git a/consensus/src/dag/reachability/inquirer.rs b/consensus/src/dag/reachability/inquirer.rs new file mode 100644 index 0000000000..022a71074b --- /dev/null +++ b/consensus/src/dag/reachability/inquirer.rs @@ -0,0 +1,345 @@ +use super::{tree::*, *}; +use crate::consensusdb::schemadb::{ReachabilityStore, ReachabilityStoreReader}; +use crate::dag::types::{interval::Interval, perf}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash; + +/// Init the reachability store to match the state required by the algorithmic layer. +/// The function first checks the store for possibly being initialized already. +pub fn init(store: &mut (impl ReachabilityStore + ?Sized)) -> Result<()> { + init_with_params(store, Hash::new(blockhash::ORIGIN), Interval::maximal()) +} + +pub(super) fn init_with_params( + store: &mut (impl ReachabilityStore + ?Sized), + origin: Hash, + capacity: Interval, +) -> Result<()> { + if store.has(origin)? { + return Ok(()); + } + store.init(origin, capacity)?; + Ok(()) +} + +type HashIterator<'a> = &'a mut dyn Iterator; + +/// Add a block to the DAG reachability data structures and persist using the provided `store`. +pub fn add_block( + store: &mut (impl ReachabilityStore + ?Sized), + new_block: Hash, + selected_parent: Hash, + mergeset_iterator: HashIterator, +) -> Result<()> { + add_block_with_params( + store, + new_block, + selected_parent, + mergeset_iterator, + None, + None, + ) +} + +fn add_block_with_params( + store: &mut (impl ReachabilityStore + ?Sized), + new_block: Hash, + selected_parent: Hash, + mergeset_iterator: HashIterator, + reindex_depth: Option, + reindex_slack: Option, +) -> Result<()> { + add_tree_block( + store, + new_block, + selected_parent, + reindex_depth.unwrap_or(perf::DEFAULT_REINDEX_DEPTH), + reindex_slack.unwrap_or(perf::DEFAULT_REINDEX_SLACK), + )?; + add_dag_block(store, new_block, mergeset_iterator)?; + Ok(()) +} + +fn add_dag_block( + store: &mut (impl ReachabilityStore + ?Sized), + new_block: Hash, + mergeset_iterator: HashIterator, +) -> Result<()> { + // Update the future covering set for blocks in the mergeset + for merged_block in mergeset_iterator { + insert_to_future_covering_set(store, merged_block, new_block)?; + } + Ok(()) +} + +fn insert_to_future_covering_set( + store: &mut (impl ReachabilityStore + ?Sized), + merged_block: Hash, + new_block: Hash, +) -> Result<()> { + match binary_search_descendant( + store, + store.get_future_covering_set(merged_block)?.as_slice(), + new_block, + )? { + // We expect the query to not succeed, and to only return the correct insertion index. + // The existences of a `future covering item` (`FCI`) which is a chain ancestor of `new_block` + // contradicts `merged_block ∈ mergeset(new_block)`. Similarly, the existence of an FCI + // which `new_block` is a chain ancestor of, contradicts processing order. + SearchOutput::Found(_, _) => Err(ReachabilityError::DataInconsistency), + SearchOutput::NotFound(i) => { + store.insert_future_covering_item(merged_block, new_block, i)?; + Ok(()) + } + } +} + +/// Hint to the reachability algorithm that `hint` is a candidate to become +/// the `virtual selected parent` (`VSP`). This might affect internal reachability heuristics such +/// as moving the reindex point. The consensus runtime is expected to call this function +/// for a new header selected tip which is `header only` / `pending UTXO verification`, or for a completely resolved `VSP`. +pub fn hint_virtual_selected_parent( + store: &mut (impl ReachabilityStore + ?Sized), + hint: Hash, +) -> Result<()> { + try_advancing_reindex_root( + store, + hint, + perf::DEFAULT_REINDEX_DEPTH, + perf::DEFAULT_REINDEX_SLACK, + ) +} + +/// Checks if the `this` block is a strict chain ancestor of the `queried` block (aka `this ∈ chain(queried)`). +/// Note that this results in `false` if `this == queried` +pub fn is_strict_chain_ancestor_of( + store: &(impl ReachabilityStoreReader + ?Sized), + this: Hash, + queried: Hash, +) -> Result { + Ok(store + .get_interval(this)? + .strictly_contains(store.get_interval(queried)?)) +} + +/// Checks if `this` block is a chain ancestor of `queried` block (aka `this ∈ chain(queried) ∪ {queried}`). +/// Note that we use the graph theory convention here which defines that a block is also an ancestor of itself. +pub fn is_chain_ancestor_of( + store: &(impl ReachabilityStoreReader + ?Sized), + this: Hash, + queried: Hash, +) -> Result { + Ok(store + .get_interval(this)? + .contains(store.get_interval(queried)?)) +} + +/// Returns true if `this` is a DAG ancestor of `queried` (aka `queried ∈ future(this) ∪ {this}`). +/// Note: this method will return true if `this == queried`. +/// The complexity of this method is O(log(|future_covering_set(this)|)) +pub fn is_dag_ancestor_of( + store: &(impl ReachabilityStoreReader + ?Sized), + this: Hash, + queried: Hash, +) -> Result { + // First, check if `this` is a chain ancestor of queried + if is_chain_ancestor_of(store, this, queried)? { + return Ok(true); + } + // Otherwise, use previously registered future blocks to complete the + // DAG reachability test + match binary_search_descendant( + store, + store.get_future_covering_set(this)?.as_slice(), + queried, + )? { + SearchOutput::Found(_, _) => Ok(true), + SearchOutput::NotFound(_) => Ok(false), + } +} + +/// Finds the child of `ancestor` which is also a chain ancestor of `descendant`. +pub fn get_next_chain_ancestor( + store: &(impl ReachabilityStoreReader + ?Sized), + descendant: Hash, + ancestor: Hash, +) -> Result { + if descendant == ancestor { + // The next ancestor does not exist + return Err(ReachabilityError::BadQuery); + } + if !is_strict_chain_ancestor_of(store, ancestor, descendant)? { + // `ancestor` isn't actually a chain ancestor of `descendant`, so by def + // we cannot find the next ancestor as well + return Err(ReachabilityError::BadQuery); + } + + get_next_chain_ancestor_unchecked(store, descendant, ancestor) +} + +/// Note: it is important to keep the unchecked version for internal module use, +/// since in some scenarios during reindexing `descendant` might have a modified +/// interval which was not propagated yet. +pub(super) fn get_next_chain_ancestor_unchecked( + store: &(impl ReachabilityStoreReader + ?Sized), + descendant: Hash, + ancestor: Hash, +) -> Result { + match binary_search_descendant(store, store.get_children(ancestor)?.as_slice(), descendant)? { + SearchOutput::Found(hash, _) => Ok(hash), + SearchOutput::NotFound(_) => Err(ReachabilityError::BadQuery), + } +} + +enum SearchOutput { + NotFound(usize), // `usize` is the position to insert at + Found(Hash, usize), +} + +fn binary_search_descendant( + store: &(impl ReachabilityStoreReader + ?Sized), + ordered_hashes: &[Hash], + descendant: Hash, +) -> Result { + if cfg!(debug_assertions) { + // This is a linearly expensive assertion, keep it debug only + assert_hashes_ordered(store, ordered_hashes); + } + + // `Interval::end` represents the unique number allocated to this block + let point = store.get_interval(descendant)?.end; + + // We use an `unwrap` here since otherwise we need to implement `binary_search` + // ourselves, which is not worth the effort given that this would be an unrecoverable + // error anyhow + match ordered_hashes.binary_search_by_key(&point, |c| store.get_interval(*c).unwrap().start) { + Ok(i) => Ok(SearchOutput::Found(ordered_hashes[i], i)), + Err(i) => { + // `i` is where `point` was expected (i.e., point < ordered_hashes[i].interval.start), + // so we expect `ordered_hashes[i - 1].interval` to be the only candidate to contain `point` + if i > 0 + && is_chain_ancestor_of( + store, + ordered_hashes[i.checked_sub(1).unwrap()], + descendant, + )? + { + Ok(SearchOutput::Found( + ordered_hashes[i.checked_sub(1).unwrap()], + i.checked_sub(1).unwrap(), + )) + } else { + Ok(SearchOutput::NotFound(i)) + } + } + } +} + +fn assert_hashes_ordered(store: &(impl ReachabilityStoreReader + ?Sized), ordered_hashes: &[Hash]) { + let intervals: Vec = ordered_hashes + .iter() + .cloned() + .map(|c| store.get_interval(c).unwrap()) + .collect(); + debug_assert!(intervals + .as_slice() + .windows(2) + .all(|w| w[0].end < w[1].start)) +} + +#[cfg(test)] +mod tests { + use super::{super::tests::*, *}; + use crate::consensusdb::schemadb::MemoryReachabilityStore; + use starcoin_types::blockhash::ORIGIN; + + #[test] + fn test_add_tree_blocks() { + // Arrange + let mut store = MemoryReachabilityStore::new(); + // Act + let root: Hash = 1.into(); + TreeBuilder::new(&mut store) + .init_with_params(root, Interval::new(1, 15)) + .add_block(2.into(), root) + .add_block(3.into(), 2.into()) + .add_block(4.into(), 2.into()) + .add_block(5.into(), 3.into()) + .add_block(6.into(), 5.into()) + .add_block(7.into(), 1.into()) + .add_block(8.into(), 6.into()) + .add_block(9.into(), 6.into()) + .add_block(10.into(), 6.into()) + .add_block(11.into(), 6.into()); + // Assert + store.validate_intervals(root).unwrap(); + } + + #[test] + fn test_add_early_blocks() { + // Arrange + let mut store = MemoryReachabilityStore::new(); + + // Act + let root: Hash = Hash::from_u64(1); + let mut builder = TreeBuilder::new_with_params(&mut store, 2, 5); + builder.init_with_params(root, Interval::maximal()); + for i in 2u64..100 { + builder.add_block(Hash::from_u64(i), Hash::from_u64(i / 2)); + } + + // Should trigger an earlier than reindex root allocation + builder.add_block(Hash::from_u64(100), Hash::from_u64(2)); + store.validate_intervals(root).unwrap(); + } + + #[test] + fn test_add_dag_blocks() { + // Arrange + let mut store = MemoryReachabilityStore::new(); + let origin_hash = Hash::new(ORIGIN); + // Act + DagBuilder::new(&mut store) + .init() + .add_block(DagBlock::new(1.into(), vec![origin_hash])) + .add_block(DagBlock::new(2.into(), vec![1.into()])) + .add_block(DagBlock::new(3.into(), vec![1.into()])) + .add_block(DagBlock::new(4.into(), vec![2.into(), 3.into()])) + .add_block(DagBlock::new(5.into(), vec![4.into()])) + .add_block(DagBlock::new(6.into(), vec![1.into()])) + .add_block(DagBlock::new(7.into(), vec![5.into(), 6.into()])) + .add_block(DagBlock::new(8.into(), vec![1.into()])) + .add_block(DagBlock::new(9.into(), vec![1.into()])) + .add_block(DagBlock::new(10.into(), vec![7.into(), 8.into(), 9.into()])) + .add_block(DagBlock::new(11.into(), vec![1.into()])) + .add_block(DagBlock::new(12.into(), vec![11.into(), 10.into()])); + + // Assert intervals + store.validate_intervals(origin_hash).unwrap(); + + // Assert genesis + for i in 2u64..=12 { + assert!(store.in_past_of(1, i)); + } + + // Assert some futures + assert!(store.in_past_of(2, 4)); + assert!(store.in_past_of(2, 5)); + assert!(store.in_past_of(2, 7)); + assert!(store.in_past_of(5, 10)); + assert!(store.in_past_of(6, 10)); + assert!(store.in_past_of(10, 12)); + assert!(store.in_past_of(11, 12)); + + // Assert some anticones + assert!(store.are_anticone(2, 3)); + assert!(store.are_anticone(2, 6)); + assert!(store.are_anticone(3, 6)); + assert!(store.are_anticone(5, 6)); + assert!(store.are_anticone(3, 8)); + assert!(store.are_anticone(11, 2)); + assert!(store.are_anticone(11, 4)); + assert!(store.are_anticone(11, 6)); + assert!(store.are_anticone(11, 9)); + } +} diff --git a/consensus/src/dag/reachability/mod.rs b/consensus/src/dag/reachability/mod.rs new file mode 100644 index 0000000000..ceb2905b03 --- /dev/null +++ b/consensus/src/dag/reachability/mod.rs @@ -0,0 +1,50 @@ +mod extensions; +pub mod inquirer; +pub mod reachability_service; +mod reindex; +pub mod relations_service; + +#[cfg(test)] +mod tests; +mod tree; + +use crate::consensusdb::prelude::StoreError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ReachabilityError { + #[error("data store error")] + StoreError(#[from] StoreError), + + #[error("data overflow error")] + DataOverflow(String), + + #[error("data inconsistency error")] + DataInconsistency, + + #[error("query is inconsistent")] + BadQuery, +} + +impl ReachabilityError { + pub fn is_key_not_found(&self) -> bool { + matches!(self, ReachabilityError::StoreError(e) if matches!(e, StoreError::KeyNotFound(_))) + } +} + +pub type Result = std::result::Result; + +pub trait ReachabilityResultExtensions { + /// Unwraps the error into `None` if the internal error is `StoreError::KeyNotFound` or panics otherwise + fn unwrap_option(self) -> Option; +} + +impl ReachabilityResultExtensions for Result { + fn unwrap_option(self) -> Option { + match self { + Ok(value) => Some(value), + Err(err) if err.is_key_not_found() => None, + Err(err) => panic!("Unexpected reachability error: {err:?}"), + } + } +} diff --git a/consensus/src/dag/reachability/reachability_service.rs b/consensus/src/dag/reachability/reachability_service.rs new file mode 100644 index 0000000000..6b2fa643a7 --- /dev/null +++ b/consensus/src/dag/reachability/reachability_service.rs @@ -0,0 +1,315 @@ +use super::{inquirer, Result}; +use crate::consensusdb::schemadb::ReachabilityStoreReader; +use parking_lot::RwLock; +use starcoin_crypto::{HashValue as Hash, HashValue}; +use starcoin_types::blockhash; +use std::{ops::Deref, sync::Arc}; + +pub trait ReachabilityService { + fn is_chain_ancestor_of(&self, this: Hash, queried: Hash) -> bool; + fn is_dag_ancestor_of_result(&self, this: Hash, queried: Hash) -> Result; + fn is_dag_ancestor_of(&self, this: Hash, queried: Hash) -> bool; + fn is_dag_ancestor_of_any(&self, this: Hash, queried: &mut impl Iterator) -> bool; + fn is_any_dag_ancestor(&self, list: &mut impl Iterator, queried: Hash) -> bool; + fn is_any_dag_ancestor_result( + &self, + list: &mut impl Iterator, + queried: Hash, + ) -> Result; + fn get_next_chain_ancestor(&self, descendant: Hash, ancestor: Hash) -> Hash; +} + +/// Multi-threaded reachability service imp +#[derive(Clone)] +pub struct MTReachabilityService { + store: Arc>, +} + +impl MTReachabilityService { + pub fn new(store: Arc>) -> Self { + Self { store } + } +} + +impl ReachabilityService for MTReachabilityService { + fn is_chain_ancestor_of(&self, this: Hash, queried: Hash) -> bool { + let read_guard = self.store.read(); + inquirer::is_chain_ancestor_of(read_guard.deref(), this, queried).unwrap() + } + + fn is_dag_ancestor_of_result(&self, this: Hash, queried: Hash) -> Result { + let read_guard = self.store.read(); + inquirer::is_dag_ancestor_of(read_guard.deref(), this, queried) + } + + fn is_dag_ancestor_of(&self, this: Hash, queried: Hash) -> bool { + let read_guard = self.store.read(); + inquirer::is_dag_ancestor_of(read_guard.deref(), this, queried).unwrap() + } + + fn is_any_dag_ancestor(&self, list: &mut impl Iterator, queried: Hash) -> bool { + let read_guard = self.store.read(); + list.any(|hash| inquirer::is_dag_ancestor_of(read_guard.deref(), hash, queried).unwrap()) + } + + fn is_any_dag_ancestor_result( + &self, + list: &mut impl Iterator, + queried: Hash, + ) -> Result { + let read_guard = self.store.read(); + for hash in list { + if inquirer::is_dag_ancestor_of(read_guard.deref(), hash, queried)? { + return Ok(true); + } + } + Ok(false) + } + + fn is_dag_ancestor_of_any(&self, this: Hash, queried: &mut impl Iterator) -> bool { + let read_guard = self.store.read(); + queried.any(|hash| inquirer::is_dag_ancestor_of(read_guard.deref(), this, hash).unwrap()) + } + + fn get_next_chain_ancestor(&self, descendant: Hash, ancestor: Hash) -> Hash { + let read_guard = self.store.read(); + inquirer::get_next_chain_ancestor(read_guard.deref(), descendant, ancestor).unwrap() + } +} + +impl MTReachabilityService { + /// Returns a forward iterator walking up the chain-selection tree from `from_ancestor` + /// to `to_descendant`, where `to_descendant` is included if `inclusive` is set to true. + /// + /// To skip `from_ancestor` simply apply `skip(1)`. + /// + /// The caller is expected to verify that `from_ancestor` is indeed a chain ancestor of + /// `to_descendant`, otherwise the function will panic. + pub fn forward_chain_iterator( + &self, + from_ancestor: Hash, + to_descendant: Hash, + inclusive: bool, + ) -> impl Iterator { + ForwardChainIterator::new(self.store.clone(), from_ancestor, to_descendant, inclusive) + } + + /// Returns a backward iterator walking down the selected chain from `from_descendant` + /// to `to_ancestor`, where `to_ancestor` is included if `inclusive` is set to true. + /// + /// To skip `from_descendant` simply apply `skip(1)`. + /// + /// The caller is expected to verify that `to_ancestor` is indeed a chain ancestor of + /// `from_descendant`, otherwise the function will panic. + pub fn backward_chain_iterator( + &self, + from_descendant: Hash, + to_ancestor: Hash, + inclusive: bool, + ) -> impl Iterator { + BackwardChainIterator::new(self.store.clone(), from_descendant, to_ancestor, inclusive) + } + + /// Returns the default chain iterator, walking from `from` backward down the + /// selected chain until `virtual genesis` (aka `blockhash::ORIGIN`; exclusive) + pub fn default_backward_chain_iterator(&self, from: Hash) -> impl Iterator { + BackwardChainIterator::new( + self.store.clone(), + from, + HashValue::new(blockhash::ORIGIN), + false, + ) + } +} + +/// Iterator design: we currently read-lock at each movement of the iterator. +/// Other options are to keep the read guard throughout the iterator lifetime, or +/// a compromise where the lock is released every constant number of items. +struct BackwardChainIterator { + store: Arc>, + current: Option, + ancestor: Hash, + inclusive: bool, +} + +impl BackwardChainIterator { + fn new( + store: Arc>, + from_descendant: Hash, + to_ancestor: Hash, + inclusive: bool, + ) -> Self { + Self { + store, + current: Some(from_descendant), + ancestor: to_ancestor, + inclusive, + } + } +} + +impl Iterator for BackwardChainIterator { + type Item = Hash; + + fn next(&mut self) -> Option { + if let Some(current) = self.current { + if current == self.ancestor { + if self.inclusive { + self.current = None; + Some(current) + } else { + self.current = None; + None + } + } else { + debug_assert_ne!(current, HashValue::new(blockhash::NONE)); + let next = self.store.read().get_parent(current).unwrap(); + self.current = Some(next); + Some(current) + } + } else { + None + } + } +} + +struct ForwardChainIterator { + store: Arc>, + current: Option, + descendant: Hash, + inclusive: bool, +} + +impl ForwardChainIterator { + fn new( + store: Arc>, + from_ancestor: Hash, + to_descendant: Hash, + inclusive: bool, + ) -> Self { + Self { + store, + current: Some(from_ancestor), + descendant: to_descendant, + inclusive, + } + } +} + +impl Iterator for ForwardChainIterator { + type Item = Hash; + + fn next(&mut self) -> Option { + if let Some(current) = self.current { + if current == self.descendant { + if self.inclusive { + self.current = None; + Some(current) + } else { + self.current = None; + None + } + } else { + let next = inquirer::get_next_chain_ancestor( + self.store.read().deref(), + self.descendant, + current, + ) + .unwrap(); + self.current = Some(next); + Some(current) + } + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::consensusdb::schemadb::MemoryReachabilityStore; + use crate::dag::{reachability::tests::TreeBuilder, types::interval::Interval}; + + #[test] + fn test_forward_iterator() { + // Arrange + let mut store = MemoryReachabilityStore::new(); + + // Act + let root: Hash = 1.into(); + TreeBuilder::new(&mut store) + .init_with_params(root, Interval::new(1, 15)) + .add_block(2.into(), root) + .add_block(3.into(), 2.into()) + .add_block(4.into(), 2.into()) + .add_block(5.into(), 3.into()) + .add_block(6.into(), 5.into()) + .add_block(7.into(), 1.into()) + .add_block(8.into(), 6.into()) + .add_block(9.into(), 6.into()) + .add_block(10.into(), 6.into()) + .add_block(11.into(), 6.into()); + + let service = MTReachabilityService::new(Arc::new(RwLock::new(store))); + + // Exclusive + let iter = service.forward_chain_iterator(2.into(), 10.into(), false); + + // Assert + let expected_hashes = [2u64, 3, 5, 6].map(Hash::from); + assert!(expected_hashes.iter().cloned().eq(iter)); + + // Inclusive + let iter = service.forward_chain_iterator(2.into(), 10.into(), true); + + // Assert + let expected_hashes = [2u64, 3, 5, 6, 10].map(Hash::from); + assert!(expected_hashes.iter().cloned().eq(iter)); + + // Compare backward to reversed forward + let forward_iter = service.forward_chain_iterator(2.into(), 10.into(), true); + let backward_iter: Vec = service + .backward_chain_iterator(10.into(), 2.into(), true) + .collect(); + assert!(forward_iter.eq(backward_iter.iter().cloned().rev())) + } + + #[test] + fn test_iterator_boundaries() { + // Arrange & Act + let mut store = MemoryReachabilityStore::new(); + let root: Hash = 1.into(); + TreeBuilder::new(&mut store) + .init_with_params(root, Interval::new(1, 5)) + .add_block(2.into(), root); + + let service = MTReachabilityService::new(Arc::new(RwLock::new(store))); + + // Asserts + assert!([1u64, 2] + .map(Hash::from) + .iter() + .cloned() + .eq(service.forward_chain_iterator(1.into(), 2.into(), true))); + assert!([1u64] + .map(Hash::from) + .iter() + .cloned() + .eq(service.forward_chain_iterator(1.into(), 2.into(), false))); + assert!([2u64, 1] + .map(Hash::from) + .iter() + .cloned() + .eq(service.backward_chain_iterator(2.into(), root, true))); + assert!([2u64] + .map(Hash::from) + .iter() + .cloned() + .eq(service.backward_chain_iterator(2.into(), root, false))); + assert!(std::iter::once(root).eq(service.backward_chain_iterator(root, root, true))); + assert!(std::iter::empty::().eq(service.backward_chain_iterator(root, root, false))); + assert!(std::iter::once(root).eq(service.forward_chain_iterator(root, root, true))); + assert!(std::iter::empty::().eq(service.forward_chain_iterator(root, root, false))); + } +} diff --git a/consensus/src/dag/reachability/reindex.rs b/consensus/src/dag/reachability/reindex.rs new file mode 100644 index 0000000000..48895b602a --- /dev/null +++ b/consensus/src/dag/reachability/reindex.rs @@ -0,0 +1,684 @@ +use super::{ + extensions::ReachabilityStoreIntervalExtensions, inquirer::get_next_chain_ancestor_unchecked, *, +}; +use crate::consensusdb::schemadb::ReachabilityStore; +use crate::dag::types::interval::Interval; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{BlockHashExtensions, BlockHashMap}; +use std::collections::VecDeque; + +/// A struct used during reindex operations. It represents a temporary context +/// for caching subtree information during the *current* reindex operation only +pub(super) struct ReindexOperationContext<'a, T: ReachabilityStore + ?Sized> { + store: &'a mut T, + subtree_sizes: BlockHashMap, // Cache for subtree sizes computed during this operation + _depth: u64, + slack: u64, +} + +impl<'a, T: ReachabilityStore + ?Sized> ReindexOperationContext<'a, T> { + pub(super) fn new(store: &'a mut T, depth: u64, slack: u64) -> Self { + Self { + store, + subtree_sizes: BlockHashMap::new(), + _depth: depth, + slack, + } + } + + /// Traverses the reachability subtree that's defined by the new child + /// block and reallocates reachability interval space + /// such that another reindexing is unlikely to occur shortly + /// thereafter. It does this by traversing down the reachability + /// tree until it finds a block with an interval size that's greater than + /// its subtree size. See `propagate_interval` for further details. + pub(super) fn reindex_intervals(&mut self, new_child: Hash, reindex_root: Hash) -> Result<()> { + let mut current = new_child; + + // Search for the first ancestor with sufficient interval space + loop { + let current_interval = self.store.get_interval(current)?; + self.count_subtrees(current)?; + + // `current` has sufficient space, break and propagate + if current_interval.size() >= self.subtree_sizes[¤t] { + break; + } + + let parent = self.store.get_parent(current)?; + + if parent.is_none() { + // If we ended up here it means that there are more + // than 2^64 blocks, which shouldn't ever happen. + return Err(ReachabilityError::DataOverflow( + "missing tree + parent during reindexing. Theoretically, this + should only ever happen if there are more + than 2^64 blocks in the DAG." + .to_string(), + )); + } + + if current == reindex_root { + // Reindex root is expected to hold enough capacity as long as there are less + // than ~2^52 blocks in the DAG, which should never happen in our lifetimes + // even if block rate per second is above 100. The calculation follows from the allocation of + // 2^12 (which equals 2^64/2^52) for slack per chain block below the reindex root. + return Err(ReachabilityError::DataOverflow(format!( + "unexpected behavior: reindex root {reindex_root} is out of capacity during reindexing. + Theoretically, this should only ever happen if there are more than ~2^52 blocks in the DAG." + ))); + } + + if inquirer::is_strict_chain_ancestor_of(self.store, parent, reindex_root)? { + // In this case parent is guaranteed to have sufficient interval space, + // however we avoid reindexing the entire subtree above parent + // (which includes root and thus majority of blocks mined since) + // and use slacks along the chain up forward from parent to reindex root. + // Notes: + // 1. we set `required_allocation` = subtree size of current in order to double the + // current interval capacity + // 2. it might be the case that current is the `new_child` itself + return self.reindex_intervals_earlier_than_root( + current, + reindex_root, + parent, + self.subtree_sizes[¤t], + ); + } + + current = parent + } + + self.propagate_interval(current) + } + + /// + /// Core (BFS) algorithms used during reindexing (see `count_subtrees` and `propagate_interval` below) + /// + /// + /// count_subtrees counts the size of each subtree under this block, + /// and populates self.subtree_sizes with the results. + /// It is equivalent to the following recursive implementation: + /// + /// fn count_subtrees(&mut self, block: Hash) -> Result { + /// let mut subtree_size = 0u64; + /// for child in self.store.get_children(block)?.iter().cloned() { + /// subtree_size += self.count_subtrees(child)?; + /// } + /// self.subtree_sizes.insert(block, subtree_size + 1); + /// Ok(subtree_size + 1) + /// } + /// + /// However, we are expecting (linearly) deep trees, and so a + /// recursive stack-based approach is inefficient and will hit + /// recursion limits. Instead, the same logic was implemented + /// using a (queue-based) BFS method. At a high level, the + /// algorithm uses BFS for reaching all leaves and pushes + /// intermediate updates from leaves via parent chains until all + /// size information is gathered at the root of the operation + /// (i.e. at block). + fn count_subtrees(&mut self, block: Hash) -> Result<()> { + if self.subtree_sizes.contains_key(&block) { + return Ok(()); + } + + let mut queue = VecDeque::::from([block]); + let mut counts = BlockHashMap::::new(); + + while let Some(mut current) = queue.pop_front() { + let children = self.store.get_children(current)?; + if children.is_empty() { + // We reached a leaf + self.subtree_sizes.insert(current, 1); + } else if !self.subtree_sizes.contains_key(¤t) { + // We haven't yet calculated the subtree size of + // the current block. Add all its children to the + // queue + queue.extend(children.iter()); + continue; + } + + // We reached a leaf or a pre-calculated subtree. + // Push information up + while current != block { + current = self.store.get_parent(current)?; + + let count = counts.entry(current).or_insert(0); + let children = self.store.get_children(current)?; + + *count = (*count).checked_add(1).unwrap(); + if *count < children.len() as u64 { + // Not all subtrees of the current block are ready + break; + } + + // All children of `current` have calculated their subtree size. + // Sum them all together and add 1 to get the sub tree size of + // `current`. + let subtree_sum: u64 = children.iter().map(|c| self.subtree_sizes[c]).sum(); + self.subtree_sizes + .insert(current, subtree_sum.checked_add(1).unwrap()); + } + } + + Ok(()) + } + + /// Propagates a new interval using a BFS traversal. + /// Subtree intervals are recursively allocated according to subtree sizes and + /// the allocation rule in `Interval::split_exponential`. + fn propagate_interval(&mut self, block: Hash) -> Result<()> { + // Make sure subtrees are counted before propagating + self.count_subtrees(block)?; + + let mut queue = VecDeque::::from([block]); + while let Some(current) = queue.pop_front() { + let children = self.store.get_children(current)?; + if !children.is_empty() { + let sizes: Vec = children.iter().map(|c| self.subtree_sizes[c]).collect(); + let interval = self.store.interval_children_capacity(current)?; + let intervals = interval.split_exponential(&sizes); + for (c, ci) in children.iter().copied().zip(intervals) { + self.store.set_interval(c, ci)?; + } + queue.extend(children.iter()); + } + } + Ok(()) + } + + /// This method implements the reindex algorithm for the case where the + /// new child node is not in reindex root's subtree. The function is expected to allocate + /// `required_allocation` to be added to interval of `allocation_block`. `common_ancestor` is + /// expected to be a direct parent of `allocation_block` and an ancestor of current `reindex_root`. + fn reindex_intervals_earlier_than_root( + &mut self, + allocation_block: Hash, + reindex_root: Hash, + common_ancestor: Hash, + required_allocation: u64, + ) -> Result<()> { + // The chosen child is: (i) child of `common_ancestor`; (ii) an + // ancestor of `reindex_root` or `reindex_root` itself + let chosen_child = + get_next_chain_ancestor_unchecked(self.store, reindex_root, common_ancestor)?; + let block_interval = self.store.get_interval(allocation_block)?; + let chosen_interval = self.store.get_interval(chosen_child)?; + + if block_interval.start < chosen_interval.start { + // `allocation_block` is in the subtree before the chosen child + self.reclaim_interval_before( + allocation_block, + common_ancestor, + chosen_child, + reindex_root, + required_allocation, + ) + } else { + // `allocation_block` is in the subtree after the chosen child + self.reclaim_interval_after( + allocation_block, + common_ancestor, + chosen_child, + reindex_root, + required_allocation, + ) + } + } + + fn reclaim_interval_before( + &mut self, + allocation_block: Hash, + common_ancestor: Hash, + chosen_child: Hash, + reindex_root: Hash, + required_allocation: u64, + ) -> Result<()> { + let mut slack_sum = 0u64; + let mut path_len = 0u64; + let mut path_slack_alloc = 0u64; + + let mut current = chosen_child; + // Walk up the chain from common ancestor's chosen child towards reindex root + loop { + if current == reindex_root { + // Reached reindex root. In this case, since we reached (the unlimited) root, + // we also re-allocate new slack for the chain we just traversed + let offset = required_allocation + .checked_add(self.slack.checked_mul(path_len).unwrap()) + .unwrap() + .checked_sub(slack_sum) + .unwrap(); + self.apply_interval_op_and_propagate(current, offset, Interval::increase_start)?; + self.offset_siblings_before(allocation_block, current, offset)?; + + // Set the slack for each chain block to be reserved below during the chain walk-down + path_slack_alloc = self.slack; + break; + } + + let slack_before_current = self.store.interval_remaining_before(current)?.size(); + slack_sum = slack_sum.checked_add(slack_before_current).unwrap(); + + if slack_sum >= required_allocation { + // Set offset to be just enough to satisfy required allocation + let offset = slack_before_current + .checked_sub(slack_sum.checked_sub(required_allocation).unwrap()) + .unwrap(); + self.apply_interval_op(current, offset, Interval::increase_start)?; + self.offset_siblings_before(allocation_block, current, offset)?; + + break; + } + + current = get_next_chain_ancestor_unchecked(self.store, reindex_root, current)?; + path_len = path_len.checked_add(1).unwrap(); + } + + // Go back down the reachability tree towards the common ancestor. + // On every hop we reindex the reachability subtree before the + // current block with an interval that is smaller. + // This is to make room for the required allocation. + loop { + current = self.store.get_parent(current)?; + if current == common_ancestor { + break; + } + + let slack_before_current = self.store.interval_remaining_before(current)?.size(); + let offset = slack_before_current.checked_sub(path_slack_alloc).unwrap(); + self.apply_interval_op(current, offset, Interval::increase_start)?; + self.offset_siblings_before(allocation_block, current, offset)?; + } + + Ok(()) + } + + fn reclaim_interval_after( + &mut self, + allocation_block: Hash, + common_ancestor: Hash, + chosen_child: Hash, + reindex_root: Hash, + required_allocation: u64, + ) -> Result<()> { + let mut slack_sum = 0u64; + let mut path_len = 0u64; + let mut path_slack_alloc = 0u64; + + let mut current = chosen_child; + // Walk up the chain from common ancestor's chosen child towards reindex root + loop { + if current == reindex_root { + // Reached reindex root. In this case, since we reached (the unlimited) root, + // we also re-allocate new slack for the chain we just traversed + let offset = required_allocation + .checked_add(self.slack.checked_mul(path_len).unwrap()) + .unwrap() + .checked_sub(slack_sum) + .unwrap(); + self.apply_interval_op_and_propagate(current, offset, Interval::decrease_end)?; + self.offset_siblings_after(allocation_block, current, offset)?; + + // Set the slack for each chain block to be reserved below during the chain walk-down + path_slack_alloc = self.slack; + break; + } + + let slack_after_current = self.store.interval_remaining_after(current)?.size(); + slack_sum = slack_sum.checked_add(slack_after_current).unwrap(); + + if slack_sum >= required_allocation { + // Set offset to be just enough to satisfy required allocation + let offset = slack_after_current + .checked_sub(slack_sum.checked_sub(required_allocation).unwrap()) + .unwrap(); + self.apply_interval_op(current, offset, Interval::decrease_end)?; + self.offset_siblings_after(allocation_block, current, offset)?; + + break; + } + + current = get_next_chain_ancestor_unchecked(self.store, reindex_root, current)?; + path_len = path_len.checked_add(1).unwrap(); + } + + // Go back down the reachability tree towards the common ancestor. + // On every hop we reindex the reachability subtree before the + // current block with an interval that is smaller. + // This is to make room for the required allocation. + loop { + current = self.store.get_parent(current)?; + if current == common_ancestor { + break; + } + + let slack_after_current = self.store.interval_remaining_after(current)?.size(); + let offset = slack_after_current.checked_sub(path_slack_alloc).unwrap(); + self.apply_interval_op(current, offset, Interval::decrease_end)?; + self.offset_siblings_after(allocation_block, current, offset)?; + } + + Ok(()) + } + + fn offset_siblings_before( + &mut self, + allocation_block: Hash, + current: Hash, + offset: u64, + ) -> Result<()> { + let parent = self.store.get_parent(current)?; + let children = self.store.get_children(parent)?; + + let (siblings_before, _) = split_children(&children, current)?; + for sibling in siblings_before.iter().cloned().rev() { + if sibling == allocation_block { + // We reached our final destination, allocate `offset` to `allocation_block` by increasing end and break + self.apply_interval_op_and_propagate( + allocation_block, + offset, + Interval::increase_end, + )?; + break; + } + // For non-`allocation_block` siblings offset the interval upwards in order to create space + self.apply_interval_op_and_propagate(sibling, offset, Interval::increase)?; + } + + Ok(()) + } + + fn offset_siblings_after( + &mut self, + allocation_block: Hash, + current: Hash, + offset: u64, + ) -> Result<()> { + let parent = self.store.get_parent(current)?; + let children = self.store.get_children(parent)?; + + let (_, siblings_after) = split_children(&children, current)?; + for sibling in siblings_after.iter().cloned() { + if sibling == allocation_block { + // We reached our final destination, allocate `offset` to `allocation_block` by decreasing only start and break + self.apply_interval_op_and_propagate( + allocation_block, + offset, + Interval::decrease_start, + )?; + break; + } + // For siblings before `allocation_block` offset the interval downwards to create space + self.apply_interval_op_and_propagate(sibling, offset, Interval::decrease)?; + } + + Ok(()) + } + + fn apply_interval_op( + &mut self, + block: Hash, + offset: u64, + op: fn(&Interval, u64) -> Interval, + ) -> Result<()> { + self.store + .set_interval(block, op(&self.store.get_interval(block)?, offset))?; + Ok(()) + } + + fn apply_interval_op_and_propagate( + &mut self, + block: Hash, + offset: u64, + op: fn(&Interval, u64) -> Interval, + ) -> Result<()> { + self.store + .set_interval(block, op(&self.store.get_interval(block)?, offset))?; + self.propagate_interval(block)?; + Ok(()) + } + + /// A method for handling reindex operations triggered by moving the reindex root + pub(super) fn concentrate_interval( + &mut self, + parent: Hash, + child: Hash, + is_final_reindex_root: bool, + ) -> Result<()> { + let children = self.store.get_children(parent)?; + + // Split the `children` of `parent` to siblings before `child` and siblings after `child` + let (siblings_before, siblings_after) = split_children(&children, child)?; + + let siblings_before_subtrees_sum: u64 = + self.tighten_intervals_before(parent, siblings_before)?; + let siblings_after_subtrees_sum: u64 = + self.tighten_intervals_after(parent, siblings_after)?; + + self.expand_interval_to_chosen( + parent, + child, + siblings_before_subtrees_sum, + siblings_after_subtrees_sum, + is_final_reindex_root, + )?; + + Ok(()) + } + + pub(super) fn tighten_intervals_before( + &mut self, + parent: Hash, + children_before: &[Hash], + ) -> Result { + let sizes = children_before + .iter() + .cloned() + .map(|block| { + self.count_subtrees(block)?; + Ok(self.subtree_sizes[&block]) + }) + .collect::>>()?; + let sum = sizes.iter().sum(); + + let interval = self.store.get_interval(parent)?; + let interval_before = Interval::new( + interval.start.checked_add(self.slack).unwrap(), + interval + .start + .checked_add(self.slack) + .unwrap() + .checked_add(sum) + .unwrap() + .checked_sub(1) + .unwrap(), + ); + + for (c, ci) in children_before + .iter() + .cloned() + .zip(interval_before.split_exact(sizes.as_slice())) + { + self.store.set_interval(c, ci)?; + self.propagate_interval(c)?; + } + + Ok(sum) + } + + pub(super) fn tighten_intervals_after( + &mut self, + parent: Hash, + children_after: &[Hash], + ) -> Result { + let sizes = children_after + .iter() + .cloned() + .map(|block| { + self.count_subtrees(block)?; + Ok(self.subtree_sizes[&block]) + }) + .collect::>>()?; + let sum = sizes.iter().sum(); + + let interval = self.store.get_interval(parent)?; + let interval_after = Interval::new( + interval + .end + .checked_sub(self.slack) + .unwrap() + .checked_sub(sum) + .unwrap(), + interval + .end + .checked_sub(self.slack) + .unwrap() + .checked_sub(1) + .unwrap(), + ); + + for (c, ci) in children_after + .iter() + .cloned() + .zip(interval_after.split_exact(sizes.as_slice())) + { + self.store.set_interval(c, ci)?; + self.propagate_interval(c)?; + } + + Ok(sum) + } + + pub(super) fn expand_interval_to_chosen( + &mut self, + parent: Hash, + child: Hash, + siblings_before_subtrees_sum: u64, + siblings_after_subtrees_sum: u64, + is_final_reindex_root: bool, + ) -> Result<()> { + let interval = self.store.get_interval(parent)?; + let allocation = Interval::new( + interval + .start + .checked_add(siblings_before_subtrees_sum) + .unwrap() + .checked_add(self.slack) + .unwrap(), + interval + .end + .checked_sub(siblings_after_subtrees_sum) + .unwrap() + .checked_sub(self.slack) + .unwrap() + .checked_sub(1) + .unwrap(), + ); + let current = self.store.get_interval(child)?; + + // Propagate interval only if the chosen `child` is the final reindex root AND + // the new interval doesn't contain the previous one + if is_final_reindex_root && !allocation.contains(current) { + /* + We deallocate slack on both sides as an optimization. Were we to + assign the fully allocated interval, the next time the reindex root moves we + would need to propagate intervals again. However when we do allocate slack, + next time this method is called (next time the reindex root moves), `allocation` is likely to contain `current`. + Note that below following the propagation we reassign the full `allocation` to `child`. + */ + let narrowed = Interval::new( + allocation.start.checked_add(self.slack).unwrap(), + allocation.end.checked_sub(self.slack).unwrap(), + ); + self.store.set_interval(child, narrowed)?; + self.propagate_interval(child)?; + } + + self.store.set_interval(child, allocation)?; + Ok(()) + } +} + +/// Splits `children` into two slices: the blocks that are before `pivot` and the blocks that are after. +fn split_children(children: &std::sync::Arc>, pivot: Hash) -> Result<(&[Hash], &[Hash])> { + if let Some(index) = children.iter().cloned().position(|c| c == pivot) { + Ok(( + &children[..index], + &children[index.checked_add(1).unwrap()..], + )) + } else { + Err(ReachabilityError::DataInconsistency) + } +} + +#[cfg(test)] +mod tests { + use super::{super::tests::*, *}; + use crate::consensusdb::schemadb::{MemoryReachabilityStore, ReachabilityStoreReader}; + use crate::dag::types::interval::Interval; + use starcoin_types::blockhash; + + #[test] + fn test_count_subtrees() { + let mut store = MemoryReachabilityStore::new(); + + // Arrange + let root: Hash = 1.into(); + StoreBuilder::new(&mut store) + .add_block(root, Hash::new(blockhash::NONE)) + .add_block(2.into(), root) + .add_block(3.into(), 2.into()) + .add_block(4.into(), 2.into()) + .add_block(5.into(), 3.into()) + .add_block(6.into(), 5.into()) + .add_block(7.into(), 1.into()) + .add_block(8.into(), 6.into()); + + // Act + let mut ctx = ReindexOperationContext::new(&mut store, 10, 16); + ctx.count_subtrees(root).unwrap(); + + // Assert + let expected = [ + (1u64, 8u64), + (2, 6), + (3, 4), + (4, 1), + (5, 3), + (6, 2), + (7, 1), + (8, 1), + ] + .iter() + .cloned() + .map(|(h, c)| (Hash::from(h), c)) + .collect::>(); + + assert_eq!(expected, ctx.subtree_sizes); + + // Act + ctx.store.set_interval(root, Interval::new(1, 8)).unwrap(); + ctx.propagate_interval(root).unwrap(); + + // Assert intervals manually + let expected_intervals = [ + (1u64, (1u64, 8u64)), + (2, (1, 6)), + (3, (1, 4)), + (4, (5, 5)), + (5, (1, 3)), + (6, (1, 2)), + (7, (7, 7)), + (8, (1, 1)), + ]; + let actual_intervals = (1u64..=8) + .map(|i| (i, ctx.store.get_interval(i.into()).unwrap().into())) + .collect::>(); + assert_eq!(actual_intervals, expected_intervals); + + // Assert intervals follow the general rules + store.validate_intervals(root).unwrap(); + } +} diff --git a/consensus/src/dag/reachability/relations_service.rs b/consensus/src/dag/reachability/relations_service.rs new file mode 100644 index 0000000000..755cfb49be --- /dev/null +++ b/consensus/src/dag/reachability/relations_service.rs @@ -0,0 +1,34 @@ +use crate::consensusdb::{prelude::StoreError, schemadb::RelationsStoreReader}; +use parking_lot::RwLock; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::BlockHashes; +use std::sync::Arc; +/// Multi-threaded block-relations service imp +#[derive(Clone)] +pub struct MTRelationsService { + store: Arc>>, + level: usize, +} + +impl MTRelationsService { + pub fn new(store: Arc>>, level: u8) -> Self { + Self { + store, + level: level as usize, + } + } +} + +impl RelationsStoreReader for MTRelationsService { + fn get_parents(&self, hash: Hash) -> Result { + self.store.read()[self.level].get_parents(hash) + } + + fn get_children(&self, hash: Hash) -> Result { + self.store.read()[self.level].get_children(hash) + } + + fn has(&self, hash: Hash) -> Result { + self.store.read()[self.level].has(hash) + } +} diff --git a/consensus/src/dag/reachability/tests.rs b/consensus/src/dag/reachability/tests.rs new file mode 100644 index 0000000000..e9fa593c86 --- /dev/null +++ b/consensus/src/dag/reachability/tests.rs @@ -0,0 +1,264 @@ +//! +//! Test utils for reachability +//! +use super::{inquirer::*, tree::*}; +use crate::consensusdb::{ + prelude::StoreError, + schemadb::{ReachabilityStore, ReachabilityStoreReader}, +}; +use crate::dag::types::{interval::Interval, perf}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{BlockHashExtensions, BlockHashMap, BlockHashSet}; +use std::collections::VecDeque; +use thiserror::Error; + +/// A struct with fluent API to streamline reachability store building +pub struct StoreBuilder<'a, T: ReachabilityStore + ?Sized> { + store: &'a mut T, +} + +impl<'a, T: ReachabilityStore + ?Sized> StoreBuilder<'a, T> { + pub fn new(store: &'a mut T) -> Self { + Self { store } + } + + pub fn add_block(&mut self, hash: Hash, parent: Hash) -> &mut Self { + let parent_height = if !parent.is_none() { + self.store.append_child(parent, hash).unwrap() + } else { + 0 + }; + self.store + .insert(hash, parent, Interval::empty(), parent_height + 1) + .unwrap(); + self + } +} + +/// A struct with fluent API to streamline tree building +pub struct TreeBuilder<'a, T: ReachabilityStore + ?Sized> { + store: &'a mut T, + reindex_depth: u64, + reindex_slack: u64, +} + +impl<'a, T: ReachabilityStore + ?Sized> TreeBuilder<'a, T> { + pub fn new(store: &'a mut T) -> Self { + Self { + store, + reindex_depth: perf::DEFAULT_REINDEX_DEPTH, + reindex_slack: perf::DEFAULT_REINDEX_SLACK, + } + } + + pub fn new_with_params(store: &'a mut T, reindex_depth: u64, reindex_slack: u64) -> Self { + Self { + store, + reindex_depth, + reindex_slack, + } + } + + pub fn init(&mut self) -> &mut Self { + init(self.store).unwrap(); + self + } + + pub fn init_with_params(&mut self, origin: Hash, capacity: Interval) -> &mut Self { + init_with_params(self.store, origin, capacity).unwrap(); + self + } + + pub fn add_block(&mut self, hash: Hash, parent: Hash) -> &mut Self { + add_tree_block( + self.store, + hash, + parent, + self.reindex_depth, + self.reindex_slack, + ) + .unwrap(); + try_advancing_reindex_root(self.store, hash, self.reindex_depth, self.reindex_slack) + .unwrap(); + self + } + + pub fn store(&self) -> &&'a mut T { + &self.store + } +} + +#[derive(Clone)] +pub struct DagBlock { + pub hash: Hash, + pub parents: Vec, +} + +impl DagBlock { + pub fn new(hash: Hash, parents: Vec) -> Self { + Self { hash, parents } + } +} + +/// A struct with fluent API to streamline DAG building +pub struct DagBuilder<'a, T: ReachabilityStore + ?Sized> { + store: &'a mut T, + map: BlockHashMap, +} + +impl<'a, T: ReachabilityStore + ?Sized> DagBuilder<'a, T> { + pub fn new(store: &'a mut T) -> Self { + Self { + store, + map: BlockHashMap::new(), + } + } + + pub fn init(&mut self) -> &mut Self { + init(self.store).unwrap(); + self + } + + pub fn add_block(&mut self, block: DagBlock) -> &mut Self { + // Select by height (longest chain) just for the sake of internal isolated tests + let selected_parent = block + .parents + .iter() + .cloned() + .max_by_key(|p| self.store.get_height(*p).unwrap()) + .unwrap(); + let mergeset = self.mergeset(&block, selected_parent); + add_block( + self.store, + block.hash, + selected_parent, + &mut mergeset.iter().cloned(), + ) + .unwrap(); + hint_virtual_selected_parent(self.store, block.hash).unwrap(); + self.map.insert(block.hash, block); + self + } + + fn mergeset(&self, block: &DagBlock, selected_parent: Hash) -> Vec { + let mut queue: VecDeque = block + .parents + .iter() + .copied() + .filter(|p| *p != selected_parent) + .collect(); + let mut mergeset: BlockHashSet = queue.iter().copied().collect(); + let mut past = BlockHashSet::new(); + + while let Some(current) = queue.pop_front() { + for parent in self.map[¤t].parents.iter() { + if mergeset.contains(parent) || past.contains(parent) { + continue; + } + + if is_dag_ancestor_of(self.store, *parent, selected_parent).unwrap() { + past.insert(*parent); + continue; + } + + mergeset.insert(*parent); + queue.push_back(*parent); + } + } + mergeset.into_iter().collect() + } + + pub fn store(&self) -> &&'a mut T { + &self.store + } +} + +#[derive(Error, Debug)] +pub enum TestError { + #[error("data store error")] + StoreError(#[from] StoreError), + + #[error("empty interval")] + EmptyInterval(Hash, Interval), + + #[error("sibling intervals are expected to be consecutive")] + NonConsecutiveSiblingIntervals(Interval, Interval), + + #[error("child interval out of parent bounds")] + IntervalOutOfParentBounds { + parent: Hash, + child: Hash, + parent_interval: Interval, + child_interval: Interval, + }, +} + +pub trait StoreValidationExtensions { + /// Checks if `block` is in the past of `other` (creates hashes from the u64 numbers) + fn in_past_of(&self, block: u64, other: u64) -> bool; + + /// Checks if `block` and `other` are in the anticone of each other + /// (creates hashes from the u64 numbers) + fn are_anticone(&self, block: u64, other: u64) -> bool; + + /// Validates that all tree intervals match the expected interval relations + fn validate_intervals(&self, root: Hash) -> std::result::Result<(), TestError>; +} + +impl StoreValidationExtensions for T { + fn in_past_of(&self, block: u64, other: u64) -> bool { + if block == other { + return false; + } + let res = is_dag_ancestor_of(self, block.into(), other.into()).unwrap(); + if res { + // Assert that the `future` relation is indeed asymmetric + assert!(!is_dag_ancestor_of(self, other.into(), block.into()).unwrap()) + } + res + } + + fn are_anticone(&self, block: u64, other: u64) -> bool { + !is_dag_ancestor_of(self, block.into(), other.into()).unwrap() + && !is_dag_ancestor_of(self, other.into(), block.into()).unwrap() + } + + fn validate_intervals(&self, root: Hash) -> std::result::Result<(), TestError> { + let mut queue = VecDeque::::from([root]); + while let Some(parent) = queue.pop_front() { + let children = self.get_children(parent)?; + queue.extend(children.iter()); + + let parent_interval = self.get_interval(parent)?; + if parent_interval.is_empty() { + return Err(TestError::EmptyInterval(parent, parent_interval)); + } + + // Verify parent-child strict relation + for child in children.iter().cloned() { + let child_interval = self.get_interval(child)?; + if !parent_interval.strictly_contains(child_interval) { + return Err(TestError::IntervalOutOfParentBounds { + parent, + child, + parent_interval, + child_interval, + }); + } + } + + // Iterate over consecutive siblings + for siblings in children.windows(2) { + let sibling_interval = self.get_interval(siblings[0])?; + let current_interval = self.get_interval(siblings[1])?; + if sibling_interval.end + 1 != current_interval.start { + return Err(TestError::NonConsecutiveSiblingIntervals( + sibling_interval, + current_interval, + )); + } + } + } + Ok(()) + } +} diff --git a/consensus/src/dag/reachability/tree.rs b/consensus/src/dag/reachability/tree.rs new file mode 100644 index 0000000000..a0d98a9b23 --- /dev/null +++ b/consensus/src/dag/reachability/tree.rs @@ -0,0 +1,161 @@ +//! +//! Tree-related functions internal to the module +//! +use super::{ + extensions::ReachabilityStoreIntervalExtensions, inquirer::*, reindex::ReindexOperationContext, + *, +}; +use crate::consensusdb::schemadb::ReachabilityStore; +use starcoin_crypto::HashValue as Hash; + +/// Adds `new_block` as a child of `parent` in the tree structure. If this block +/// has no remaining interval to allocate, a reindexing is triggered. When a reindexing +/// is triggered, the reindex root point is used within the reindex algorithm's logic +pub fn add_tree_block( + store: &mut (impl ReachabilityStore + ?Sized), + new_block: Hash, + parent: Hash, + reindex_depth: u64, + reindex_slack: u64, +) -> Result<()> { + // Get the remaining interval capacity + let remaining = store.interval_remaining_after(parent)?; + // Append the new child to `parent.children` + let parent_height = store.append_child(parent, new_block)?; + if remaining.is_empty() { + // Init with the empty interval. + // Note: internal logic relies on interval being this specific interval + // which comes exactly at the end of current capacity + store.insert( + new_block, + parent, + remaining, + parent_height.checked_add(1).unwrap(), + )?; + + // Start a reindex operation (TODO: add timing) + let reindex_root = store.get_reindex_root()?; + let mut ctx = ReindexOperationContext::new(store, reindex_depth, reindex_slack); + ctx.reindex_intervals(new_block, reindex_root)?; + } else { + let allocated = remaining.split_half().0; + store.insert( + new_block, + parent, + allocated, + parent_height.checked_add(1).unwrap(), + )?; + }; + Ok(()) +} + +/// Finds the most recent tree ancestor common to both `block` and the given `reindex root`. +/// Note that we assume that almost always the chain between the reindex root and the common +/// ancestor is longer than the chain between block and the common ancestor, hence we iterate +/// from `block`. +pub fn find_common_tree_ancestor( + store: &(impl ReachabilityStore + ?Sized), + block: Hash, + reindex_root: Hash, +) -> Result { + let mut current = block; + loop { + if is_chain_ancestor_of(store, current, reindex_root)? { + return Ok(current); + } + current = store.get_parent(current)?; + } +} + +/// Finds a possible new reindex root, based on the `current` reindex root and the selected tip `hint` +pub fn find_next_reindex_root( + store: &(impl ReachabilityStore + ?Sized), + current: Hash, + hint: Hash, + reindex_depth: u64, + reindex_slack: u64, +) -> Result<(Hash, Hash)> { + let mut ancestor = current; + let mut next = current; + + let hint_height = store.get_height(hint)?; + + // Test if current root is ancestor of selected tip (`hint`) - if not, this is a reorg case + if !is_chain_ancestor_of(store, current, hint)? { + let current_height = store.get_height(current)?; + + // We have reindex root out of (hint) selected tip chain, however we switch chains only after a sufficient + // threshold of `reindex_slack` diff in order to address possible alternating reorg attacks. + // The `reindex_slack` constant is used as an heuristic large enough on the one hand, but + // one which will not harm performance on the other hand - given the available slack at the chain split point. + // + // Note: In some cases the height of the (hint) selected tip can be lower than the current reindex root height. + // If that's the case we keep the reindex root unchanged. + if hint_height < current_height + || hint_height.checked_sub(current_height).unwrap() < reindex_slack + { + return Ok((current, current)); + } + + let common = find_common_tree_ancestor(store, hint, current)?; + ancestor = common; + next = common; + } + + // Iterate from ancestor towards the selected tip (`hint`) until passing the + // `reindex_window` threshold, for finding the new reindex root + loop { + let child = get_next_chain_ancestor_unchecked(store, hint, next)?; + let child_height = store.get_height(child)?; + + if hint_height < child_height { + return Err(ReachabilityError::DataInconsistency); + } + if hint_height.checked_sub(child_height).unwrap() < reindex_depth { + break; + } + next = child; + } + + Ok((ancestor, next)) +} + +/// Attempts to advance or move the current reindex root according to the +/// provided `virtual selected parent` (`VSP`) hint. +/// It is important for the reindex root point to follow the consensus-agreed chain +/// since this way it can benefit from chain-robustness which is implied by the security +/// of the ordering protocol. That is, it enjoys from the fact that all future blocks are +/// expected to elect the root subtree (by converging to the agreement to have it on the +/// selected chain). See also the reachability algorithms overview (TODO) +pub fn try_advancing_reindex_root( + store: &mut (impl ReachabilityStore + ?Sized), + hint: Hash, + reindex_depth: u64, + reindex_slack: u64, +) -> Result<()> { + // Get current root from the store + let current = store.get_reindex_root()?; + + // Find the possible new root + let (mut ancestor, next) = + find_next_reindex_root(store, current, hint, reindex_depth, reindex_slack)?; + + // No update to root, return + if current == next { + return Ok(()); + } + + // if ancestor == next { + // trace!("next reindex root is an ancestor of current one, skipping concentration.") + // } + while ancestor != next { + let child = get_next_chain_ancestor_unchecked(store, next, ancestor)?; + let mut ctx = ReindexOperationContext::new(store, reindex_depth, reindex_slack); + ctx.concentrate_interval(ancestor, child, child == next)?; + ancestor = child; + } + + // Update reindex root in the data store + store.set_reindex_root(next)?; + Ok(()) +} diff --git a/consensus/src/dag/types/ghostdata.rs b/consensus/src/dag/types/ghostdata.rs new file mode 100644 index 0000000000..c680172148 --- /dev/null +++ b/consensus/src/dag/types/ghostdata.rs @@ -0,0 +1,147 @@ +use super::trusted::ExternalGhostdagData; +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{BlockHashMap, BlockHashes, BlueWorkType, HashKTypeMap, KType}; +use std::sync::Arc; + +#[derive(Clone, Serialize, Deserialize, Default, Debug)] +pub struct GhostdagData { + pub blue_score: u64, + pub blue_work: BlueWorkType, + pub selected_parent: Hash, + pub mergeset_blues: BlockHashes, + pub mergeset_reds: BlockHashes, + pub blues_anticone_sizes: HashKTypeMap, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, Copy)] +pub struct CompactGhostdagData { + pub blue_score: u64, + pub blue_work: BlueWorkType, + pub selected_parent: Hash, +} + +impl From for GhostdagData { + fn from(value: ExternalGhostdagData) -> Self { + Self { + blue_score: value.blue_score, + blue_work: value.blue_work, + selected_parent: value.selected_parent, + mergeset_blues: Arc::new(value.mergeset_blues), + mergeset_reds: Arc::new(value.mergeset_reds), + blues_anticone_sizes: Arc::new(value.blues_anticone_sizes), + } + } +} + +impl From<&GhostdagData> for ExternalGhostdagData { + fn from(value: &GhostdagData) -> Self { + Self { + blue_score: value.blue_score, + blue_work: value.blue_work, + selected_parent: value.selected_parent, + mergeset_blues: (*value.mergeset_blues).clone(), + mergeset_reds: (*value.mergeset_reds).clone(), + blues_anticone_sizes: (*value.blues_anticone_sizes).clone(), + } + } +} + +impl GhostdagData { + pub fn new( + blue_score: u64, + blue_work: BlueWorkType, + selected_parent: Hash, + mergeset_blues: BlockHashes, + mergeset_reds: BlockHashes, + blues_anticone_sizes: HashKTypeMap, + ) -> Self { + Self { + blue_score, + blue_work, + selected_parent, + mergeset_blues, + mergeset_reds, + blues_anticone_sizes, + } + } + + pub fn new_with_selected_parent(selected_parent: Hash, k: KType) -> Self { + let mut mergeset_blues: Vec = Vec::with_capacity(k.checked_add(1).unwrap() as usize); + let mut blues_anticone_sizes: BlockHashMap = BlockHashMap::with_capacity(k as usize); + mergeset_blues.push(selected_parent); + blues_anticone_sizes.insert(selected_parent, 0); + + Self { + blue_score: Default::default(), + blue_work: Default::default(), + selected_parent, + mergeset_blues: BlockHashes::new(mergeset_blues), + mergeset_reds: Default::default(), + blues_anticone_sizes: HashKTypeMap::new(blues_anticone_sizes), + } + } + + pub fn mergeset_size(&self) -> usize { + self.mergeset_blues + .len() + .checked_add(self.mergeset_reds.len()) + .unwrap() + } + + /// Returns an iterator to the mergeset with no specified order (excluding the selected parent) + pub fn unordered_mergeset_without_selected_parent(&self) -> impl Iterator + '_ { + self.mergeset_blues + .iter() + .skip(1) // Skip the selected parent + .cloned() + .chain(self.mergeset_reds.iter().cloned()) + } + + /// Returns an iterator to the mergeset with no specified order (including the selected parent) + pub fn unordered_mergeset(&self) -> impl Iterator + '_ { + self.mergeset_blues + .iter() + .cloned() + .chain(self.mergeset_reds.iter().cloned()) + } + + pub fn to_compact(&self) -> CompactGhostdagData { + CompactGhostdagData { + blue_score: self.blue_score, + blue_work: self.blue_work, + selected_parent: self.selected_parent, + } + } + + pub fn add_blue( + &mut self, + block: Hash, + blue_anticone_size: KType, + block_blues_anticone_sizes: &BlockHashMap, + ) { + // Add the new blue block to mergeset blues + BlockHashes::make_mut(&mut self.mergeset_blues).push(block); + + // Get a mut ref to internal anticone size map + let blues_anticone_sizes = HashKTypeMap::make_mut(&mut self.blues_anticone_sizes); + + // Insert the new blue block with its blue anticone size to the map + blues_anticone_sizes.insert(block, blue_anticone_size); + + // Insert/update map entries for blocks affected by this insertion + for (blue, size) in block_blues_anticone_sizes { + blues_anticone_sizes.insert(*blue, size.checked_add(1).unwrap()); + } + } + + pub fn add_red(&mut self, block: Hash) { + // Add the new red block to mergeset reds + BlockHashes::make_mut(&mut self.mergeset_reds).push(block); + } + + pub fn finalize_score_and_work(&mut self, blue_score: u64, blue_work: BlueWorkType) { + self.blue_score = blue_score; + self.blue_work = blue_work; + } +} diff --git a/consensus/src/dag/types/interval.rs b/consensus/src/dag/types/interval.rs new file mode 100644 index 0000000000..0b5cc4f6e5 --- /dev/null +++ b/consensus/src/dag/types/interval.rs @@ -0,0 +1,377 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; + +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub struct Interval { + pub start: u64, + pub end: u64, +} + +impl Display for Interval { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "[{}, {}]", self.start, self.end) + } +} + +impl From for (u64, u64) { + fn from(val: Interval) -> Self { + (val.start, val.end) + } +} + +impl Interval { + pub fn new(start: u64, end: u64) -> Self { + debug_assert!(start > 0 && end < u64::MAX && end >= start.checked_sub(1).unwrap()); // TODO: make sure this is actually debug-only + Interval { start, end } + } + + pub fn empty() -> Self { + Self::new(1, 0) + } + + /// Returns the maximally allowed `u64` interval. We leave a margin of 1 from + /// both `u64` bounds (`0` and `u64::MAX`) in order to support the reduction of any + /// legal interval to an empty one by setting `end = start - 1` or `start = end + 1` + pub fn maximal() -> Self { + Self::new(1, u64::MAX.saturating_sub(1)) + } + + pub fn size(&self) -> u64 { + // Empty intervals are indicated by `self.end == self.start - 1`, so + // we avoid the overflow by first adding 1 + // Note: this function will panic if `self.end < self.start - 1` due to overflow + (self.end.checked_add(1).unwrap()) + .checked_sub(self.start) + .unwrap() + } + + pub fn is_empty(&self) -> bool { + self.size() == 0 + } + + pub fn increase(&self, offset: u64) -> Self { + Self::new( + self.start.checked_add(offset).unwrap(), + self.end.checked_add(offset).unwrap(), + ) + } + + pub fn decrease(&self, offset: u64) -> Self { + Self::new( + self.start.checked_sub(offset).unwrap(), + self.end.checked_sub(offset).unwrap(), + ) + } + + pub fn increase_start(&self, offset: u64) -> Self { + Self::new(self.start.checked_add(offset).unwrap(), self.end) + } + + pub fn decrease_start(&self, offset: u64) -> Self { + Self::new(self.start.checked_sub(offset).unwrap(), self.end) + } + + pub fn increase_end(&self, offset: u64) -> Self { + Self::new(self.start, self.end.checked_add(offset).unwrap()) + } + + pub fn decrease_end(&self, offset: u64) -> Self { + Self::new(self.start, self.end.checked_sub(offset).unwrap()) + } + + pub fn split_half(&self) -> (Self, Self) { + self.split_fraction(0.5) + } + + /// Splits this interval to two parts such that their + /// union is equal to the original interval and the first (left) part + /// contains the given fraction of the original interval's size. + /// Note: if the split results in fractional parts, this method rounds + /// the first part up and the last part down. + fn split_fraction(&self, fraction: f32) -> (Self, Self) { + let left_size = f32::ceil(self.size() as f32 * fraction) as u64; + + ( + Self::new( + self.start, + self.start + .checked_add(left_size) + .unwrap() + .checked_sub(1) + .unwrap(), + ), + Self::new(self.start.checked_add(left_size).unwrap(), self.end), + ) + } + + /// Splits this interval to exactly |sizes| parts where + /// |part_i| = sizes[i]. This method expects sum(sizes) to be exactly + /// equal to the interval's size. + pub fn split_exact(&self, sizes: &[u64]) -> Vec { + assert_eq!( + sizes.iter().sum::(), + self.size(), + "sum of sizes must be equal to the interval's size" + ); + let mut start = self.start; + sizes + .iter() + .map(|size| { + let interval = Self::new( + start, + start.checked_add(*size).unwrap().checked_sub(1).unwrap(), + ); + start = start.checked_add(*size).unwrap(); + interval + }) + .collect() + } + + /// Splits this interval to |sizes| parts + /// by the allocation rule described below. This method expects sum(sizes) + /// to be smaller or equal to the interval's size. Every part_i is + /// allocated at least sizes[i] capacity. The remaining budget is + /// split by an exponentially biased rule described below. + /// + /// This rule follows the GHOSTDAG protocol behavior where the child + /// with the largest subtree is expected to dominate the competition + /// for new blocks and thus grow the most. However, we may need to + /// add slack for non-largest subtrees in order to make CPU reindexing + /// attacks unworthy. + pub fn split_exponential(&self, sizes: &[u64]) -> Vec { + let interval_size = self.size(); + let sizes_sum = sizes.iter().sum::(); + assert!( + interval_size >= sizes_sum, + "interval's size must be greater than or equal to sum of sizes" + ); + assert!(sizes_sum > 0, "cannot split to 0 parts"); + if interval_size == sizes_sum { + return self.split_exact(sizes); + } + + // + // Add a fractional bias to every size in the provided sizes + // + + let mut remaining_bias = interval_size.checked_sub(sizes_sum).unwrap(); + let total_bias = remaining_bias as f64; + + let mut biased_sizes = Vec::::with_capacity(sizes.len()); + let exp_fractions = exponential_fractions(sizes); + for (i, fraction) in exp_fractions.iter().enumerate() { + let bias: u64 = if i == exp_fractions.len().checked_sub(1).unwrap() { + remaining_bias + } else { + remaining_bias.min(f64::round(total_bias * fraction) as u64) + }; + biased_sizes.push(sizes[i].checked_add(bias).unwrap()); + remaining_bias = remaining_bias.checked_sub(bias).unwrap(); + } + + self.split_exact(biased_sizes.as_slice()) + } + + pub fn contains(&self, other: Self) -> bool { + self.start <= other.start && other.end <= self.end + } + + pub fn strictly_contains(&self, other: Self) -> bool { + self.start <= other.start && other.end < self.end + } +} + +/// Returns a fraction for each size in sizes +/// as follows: +/// fraction[i] = 2^size[i] / sum_j(2^size[j]) +/// In the code below the above equation is divided by 2^max(size) +/// to avoid exploding numbers. Note that in 1 / 2^(max(size)-size[i]) +/// we divide 1 by potentially a very large number, which will +/// result in loss of float precision. This is not a problem - all +/// numbers close to 0 bear effectively the same weight. +fn exponential_fractions(sizes: &[u64]) -> Vec { + let max_size = sizes.iter().copied().max().unwrap_or_default(); + + let mut fractions = sizes + .iter() + .map(|s| 1f64 / 2f64.powf((max_size - s) as f64)) + .collect::>(); + + let fractions_sum = fractions.iter().sum::(); + for item in &mut fractions { + *item /= fractions_sum; + } + + fractions +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_interval_basics() { + let interval = Interval::new(101, 164); + let increased = interval.increase(10); + let decreased = increased.decrease(5); + // println!("{}", interval.clone()); + + assert_eq!(interval.start + 10, increased.start); + assert_eq!(interval.end + 10, increased.end); + + assert_eq!(interval.start + 5, decreased.start); + assert_eq!(interval.end + 5, decreased.end); + + assert_eq!(interval.size(), 64); + assert_eq!(Interval::maximal().size(), u64::MAX - 1); + assert_eq!(Interval::empty().size(), 0); + + let (empty_left, empty_right) = Interval::empty().split_half(); + assert_eq!(empty_left.size(), 0); + assert_eq!(empty_right.size(), 0); + + assert_eq!(interval.start + 10, interval.increase_start(10).start); + assert_eq!(interval.start - 10, interval.decrease_start(10).start); + assert_eq!(interval.end + 10, interval.increase_end(10).end); + assert_eq!(interval.end - 10, interval.decrease_end(10).end); + + assert_eq!(interval.end, interval.increase_start(10).end); + assert_eq!(interval.end, interval.decrease_start(10).end); + assert_eq!(interval.start, interval.increase_end(10).start); + assert_eq!(interval.start, interval.decrease_end(10).start); + + // println!("{:?}", Interval::maximal()); + // println!("{:?}", Interval::maximal().split_half()); + } + + #[test] + fn test_split_exact() { + let sizes = vec![5u64, 10, 15, 20]; + let intervals = Interval::new(1, 50).split_exact(sizes.as_slice()); + assert_eq!(intervals.len(), sizes.len()); + for i in 0..sizes.len() { + assert_eq!(intervals[i].size(), sizes[i]) + } + } + + #[test] + fn test_exponential_fractions() { + let mut exp_fractions = exponential_fractions(vec![2, 4, 8, 16].as_slice()); + // println!("{:?}", exp_fractions); + for i in 0..exp_fractions.len() - 1 { + assert!(exp_fractions[i + 1] > exp_fractions[i]); + } + + exp_fractions = exponential_fractions(vec![].as_slice()); + assert_eq!(exp_fractions.len(), 0); + + exp_fractions = exponential_fractions(vec![0, 0].as_slice()); + assert_eq!(exp_fractions.len(), 2); + assert_eq!(0.5f64, exp_fractions[0]); + assert_eq!(exp_fractions[0], exp_fractions[1]); + } + + #[test] + fn test_contains() { + assert!(Interval::new(1, 100).contains(Interval::new(1, 100))); + assert!(Interval::new(1, 100).contains(Interval::new(1, 99))); + assert!(Interval::new(1, 100).contains(Interval::new(2, 100))); + assert!(Interval::new(1, 100).contains(Interval::new(2, 99))); + assert!(!Interval::new(1, 100).contains(Interval::new(50, 150))); + assert!(!Interval::new(1, 100).contains(Interval::new(150, 160))); + } + + #[test] + fn test_split_exponential() { + struct Test { + interval: Interval, + sizes: Vec, + expected: Vec, + } + + let tests = [ + Test { + interval: Interval::new(1, 100), + sizes: vec![100u64], + expected: vec![Interval::new(1, 100)], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![50u64, 50], + expected: vec![Interval::new(1, 50), Interval::new(51, 100)], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![10u64, 20, 30, 40], + expected: vec![ + Interval::new(1, 10), + Interval::new(11, 30), + Interval::new(31, 60), + Interval::new(61, 100), + ], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![25u64, 25], + expected: vec![Interval::new(1, 50), Interval::new(51, 100)], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![1u64, 1], + expected: vec![Interval::new(1, 50), Interval::new(51, 100)], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![33u64, 33, 33], + expected: vec![ + Interval::new(1, 33), + Interval::new(34, 66), + Interval::new(67, 100), + ], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![10u64, 15, 25], + expected: vec![ + Interval::new(1, 10), + Interval::new(11, 25), + Interval::new(26, 100), + ], + }, + Test { + interval: Interval::new(1, 100), + sizes: vec![25u64, 15, 10], + expected: vec![ + Interval::new(1, 75), + Interval::new(76, 90), + Interval::new(91, 100), + ], + }, + Test { + interval: Interval::new(1, 10_000), + sizes: vec![10u64, 10, 20], + expected: vec![ + Interval::new(1, 20), + Interval::new(21, 40), + Interval::new(41, 10_000), + ], + }, + Test { + interval: Interval::new(1, 100_000), + sizes: vec![31_000u64, 31_000, 30_001], + expected: vec![ + Interval::new(1, 35_000), + Interval::new(35_001, 69_999), + Interval::new(70_000, 100_000), + ], + }, + ]; + + for test in &tests { + assert_eq!( + test.expected, + test.interval.split_exponential(test.sizes.as_slice()) + ); + } + } +} diff --git a/consensus/src/dag/types/mod.rs b/consensus/src/dag/types/mod.rs new file mode 100644 index 0000000000..d3acae1c23 --- /dev/null +++ b/consensus/src/dag/types/mod.rs @@ -0,0 +1,6 @@ +pub mod ghostdata; +pub mod interval; +pub mod ordering; +pub mod perf; +pub mod reachability; +pub mod trusted; diff --git a/consensus/src/dag/types/ordering.rs b/consensus/src/dag/types/ordering.rs new file mode 100644 index 0000000000..a1ed8c2561 --- /dev/null +++ b/consensus/src/dag/types/ordering.rs @@ -0,0 +1,36 @@ +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::BlueWorkType; +use std::cmp::Ordering; + +#[derive(Eq, Clone, Debug, Serialize, Deserialize)] +pub struct SortableBlock { + pub hash: Hash, + pub blue_work: BlueWorkType, +} + +impl SortableBlock { + pub fn new(hash: Hash, blue_work: BlueWorkType) -> Self { + Self { hash, blue_work } + } +} + +impl PartialEq for SortableBlock { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash + } +} + +impl PartialOrd for SortableBlock { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SortableBlock { + fn cmp(&self, other: &Self) -> Ordering { + self.blue_work + .cmp(&other.blue_work) + .then_with(|| self.hash.cmp(&other.hash)) + } +} diff --git a/consensus/src/dag/types/perf.rs b/consensus/src/dag/types/perf.rs new file mode 100644 index 0000000000..6da44d4cd7 --- /dev/null +++ b/consensus/src/dag/types/perf.rs @@ -0,0 +1,51 @@ +//! +//! A module for performance critical constants which depend on consensus parameters. +//! The constants in this module should all be revisited if mainnet consensus parameters change. +//! + +/// The default target depth for reachability reindexes. +pub const DEFAULT_REINDEX_DEPTH: u64 = 100; + +/// The default slack interval used by the reachability +/// algorithm to encounter for blocks out of the selected chain. +pub const DEFAULT_REINDEX_SLACK: u64 = 1 << 12; + +#[derive(Clone, Debug)] +pub struct PerfParams { + // + // Cache sizes + // + /// Preferred cache size for header-related data + pub header_data_cache_size: u64, + + /// Preferred cache size for block-body-related data which + /// is typically orders-of magnitude larger than header data + /// (Note this cannot be set to high due to severe memory consumption) + pub block_data_cache_size: u64, + + /// Preferred cache size for UTXO-related data + pub utxo_set_cache_size: u64, + + /// Preferred cache size for block-window-related data + pub block_window_cache_size: u64, + + // + // Thread-pools + // + /// Defaults to 0 which indicates using system default + /// which is typically the number of logical CPU cores + pub block_processors_num_threads: usize, + + /// Defaults to 0 which indicates using system default + /// which is typically the number of logical CPU cores + pub virtual_processor_num_threads: usize, +} + +pub const PERF_PARAMS: PerfParams = PerfParams { + header_data_cache_size: 10_000, + block_data_cache_size: 200, + utxo_set_cache_size: 10_000, + block_window_cache_size: 2000, + block_processors_num_threads: 0, + virtual_processor_num_threads: 0, +}; diff --git a/consensus/src/dag/types/reachability.rs b/consensus/src/dag/types/reachability.rs new file mode 100644 index 0000000000..35dc3979b6 --- /dev/null +++ b/consensus/src/dag/types/reachability.rs @@ -0,0 +1,26 @@ +use super::interval::Interval; +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::BlockHashes; +use std::sync::Arc; + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct ReachabilityData { + pub children: BlockHashes, + pub parent: Hash, + pub interval: Interval, + pub height: u64, + pub future_covering_set: BlockHashes, +} + +impl ReachabilityData { + pub fn new(parent: Hash, interval: Interval, height: u64) -> Self { + Self { + children: Arc::new(vec![]), + parent, + interval, + height, + future_covering_set: Arc::new(vec![]), + } + } +} diff --git a/consensus/src/dag/types/trusted.rs b/consensus/src/dag/types/trusted.rs new file mode 100644 index 0000000000..9a4cf37bbd --- /dev/null +++ b/consensus/src/dag/types/trusted.rs @@ -0,0 +1,26 @@ +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue as Hash; +use starcoin_types::blockhash::{BlockHashMap, BlueWorkType, KType}; + +/// Represents semi-trusted externally provided Ghostdag data (by a network peer) +#[derive(Clone, Serialize, Deserialize)] +pub struct ExternalGhostdagData { + pub blue_score: u64, + pub blue_work: BlueWorkType, + pub selected_parent: Hash, + pub mergeset_blues: Vec, + pub mergeset_reds: Vec, + pub blues_anticone_sizes: BlockHashMap, +} + +/// Represents externally provided Ghostdag data associated with a block Hash +pub struct TrustedGhostdagData { + pub hash: Hash, + pub ghostdag: ExternalGhostdagData, +} + +impl TrustedGhostdagData { + pub fn new(hash: Hash, ghostdag: ExternalGhostdagData) -> Self { + Self { hash, ghostdag } + } +} diff --git a/executor/benchmark/src/lib.rs b/executor/benchmark/src/lib.rs index 3706971393..87aaa90f63 100644 --- a/executor/benchmark/src/lib.rs +++ b/executor/benchmark/src/lib.rs @@ -256,7 +256,7 @@ pub fn run_benchmark( let chain_state = ChainStateDB::new(storage, None); let net = ChainNetwork::new_test(); let genesis_txn = Genesis::build_genesis_transaction(&net).unwrap(); - let _ = Genesis::execute_genesis_txn(&chain_state, genesis_txn).unwrap(); + let _txn_info = Genesis::execute_genesis_txn(&chain_state, genesis_txn).unwrap(); let (block_sender, block_receiver) = mpsc::sync_channel(50 /* bound */); diff --git a/network-rpc/api/src/dag_protocol.rs b/network-rpc/api/src/dag_protocol.rs new file mode 100644 index 0000000000..c47b28ac52 --- /dev/null +++ b/network-rpc/api/src/dag_protocol.rs @@ -0,0 +1,49 @@ +use network_p2p_core::PeerId; +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue; +use starcoin_types::block::Block; + +#[derive(Clone, Debug, Hash, Eq, PartialOrd, Ord, PartialEq, Serialize, Deserialize)] +pub struct RelationshipPair { + pub parent: HashValue, + pub child: HashValue, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] +pub struct GetDagAccumulatorLeaves { + pub accumulator_leaf_index: u64, + pub batch_size: u64, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] +pub struct TargetDagAccumulatorLeaf { + pub accumulator_root: HashValue, // accumulator info root + pub leaf_index: u64, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] +pub struct GetTargetDagAccumulatorLeafDetail { + pub leaf_index: u64, + pub batch_size: u64, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] +pub struct TargetDagAccumulatorLeafDetail { + pub accumulator_root: HashValue, + pub tips: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct GetSyncDagBlockInfo { + pub leaf_index: u64, + pub batch_size: u64, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct SyncDagBlockInfo { + pub block_id: HashValue, + pub block: Option, + pub peer_id: Option, + pub dag_parents: Vec, + pub dag_transaction_header: Option, +} diff --git a/node/src/node.rs b/node/src/node.rs index 5e00d55aa6..6eae38c4c6 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -13,6 +13,7 @@ use futures::executor::block_on; use futures_timer::Delay; use network_api::{PeerProvider, PeerSelector, PeerStrategy}; use starcoin_account_service::{AccountEventService, AccountService, AccountStorage}; +use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_block_relayer::BlockRelayer; use starcoin_chain_notify::ChainNotifyHandlerService; use starcoin_chain_service::ChainReaderService; @@ -45,7 +46,7 @@ use starcoin_storage::db_storage::DBStorage; use starcoin_storage::errors::StorageInitError; use starcoin_storage::metrics::StorageMetrics; use starcoin_storage::storage::StorageInstance; -use starcoin_storage::{BlockStore, Storage}; +use starcoin_storage::{BlockStore, Storage, Store}; use starcoin_stratum::service::{StratumService, StratumServiceFactory}; use starcoin_stratum::stratum::{Stratum, StratumFactory}; use starcoin_sync::announcement::AnnouncementService; @@ -55,7 +56,7 @@ use starcoin_sync::txn_sync::TxnSyncService; use starcoin_sync::verified_rpc_client::VerifiedRpcClient; use starcoin_txpool::{TxPoolActorService, TxPoolService}; use starcoin_types::blockhash::ORIGIN; -use starcoin_types::header::Header; +use starcoin_types::header::DagHeader; use starcoin_types::system_events::{SystemShutdown, SystemStarted}; use starcoin_vm_runtime::metrics::VMMetrics; use std::sync::{Arc, Mutex}; @@ -371,7 +372,7 @@ impl NodeService { .expect("Failed to create flexidag storage"); let dag = BlockDAG::new( - Header::new( + DagHeader::new( genesis.block().header().clone(), vec![HashValue::new(ORIGIN)], ), diff --git a/storage/src/flexi_dag/mod.rs b/storage/src/flexi_dag/mod.rs new file mode 100644 index 0000000000..c3333272d7 --- /dev/null +++ b/storage/src/flexi_dag/mod.rs @@ -0,0 +1,76 @@ +use std::sync::Arc; + +use crate::{ + accumulator::{AccumulatorStorage, DagBlockAccumulatorStorage}, + define_storage, + storage::{CodecKVStore, StorageInstance, ValueCodec}, + SYNC_FLEXI_DAG_SNAPSHOT_PREFIX_NAME, +}; +use anyhow::Result; +use bcs_ext::BCSCodec; +use serde::{Deserialize, Serialize}; +use starcoin_accumulator::accumulator_info::AccumulatorInfo; +use starcoin_crypto::HashValue; + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct SyncFlexiDagSnapshot { + pub child_hashes: Vec, // child nodes(tips), to get the relationship, use dag's relationship store + pub accumulator_info: AccumulatorInfo, +} + +impl ValueCodec for SyncFlexiDagSnapshot { + fn encode_value(&self) -> Result> { + self.encode() + } + + fn decode_value(data: &[u8]) -> Result { + Self::decode(data) + } +} + +define_storage!( + SyncFlexiDagSnapshotStorage, + HashValue, // accumulator leaf node + SyncFlexiDagSnapshot, + SYNC_FLEXI_DAG_SNAPSHOT_PREFIX_NAME +); + +#[derive(Clone)] +pub struct SyncFlexiDagStorage { + snapshot_storage: Arc, + accumulator_storage: AccumulatorStorage, +} + +impl SyncFlexiDagStorage { + pub fn new(instance: StorageInstance) -> Self { + let snapshot_storage = Arc::new(SyncFlexiDagSnapshotStorage::new(instance.clone())); + let accumulator_storage = + AccumulatorStorage::::new_dag_block_accumulator_storage( + instance, + ); + + SyncFlexiDagStorage { + snapshot_storage, + accumulator_storage, + } + } + + pub fn get_accumulator_storage(&self) -> AccumulatorStorage { + self.accumulator_storage.clone() + } + + pub fn get_snapshot_storage(&self) -> Arc { + self.snapshot_storage.clone() + } + + pub fn put_hashes(&self, key: HashValue, accumulator_info: SyncFlexiDagSnapshot) -> Result<()> { + self.snapshot_storage.put(key, accumulator_info) + } + + pub fn get_hashes_by_hash( + &self, + hash: HashValue, + ) -> std::result::Result, anyhow::Error> { + self.snapshot_storage.get(hash) + } +} diff --git a/storage/src/tests/test_dag.rs b/storage/src/tests/test_dag.rs new file mode 100644 index 0000000000..159c905ba2 --- /dev/null +++ b/storage/src/tests/test_dag.rs @@ -0,0 +1,347 @@ +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_config::RocksdbConfig; +use starcoin_crypto::HashValue; + +use crate::{ + cache_storage::CacheStorage, db_storage::DBStorage, flexi_dag::SyncFlexiDagSnapshot, + storage::StorageInstance, Storage, Store, SyncFlexiDagStore, +}; +use anyhow::{Ok, Result}; + +trait SyncFlexiDagManager { + fn insert_hashes(&self, hashes: Vec) -> Result; + fn query_by_hash(&self, hash: HashValue) -> Result>; + fn fork(&mut self, accumulator_info: AccumulatorInfo) -> Result<()>; + fn get_hash_by_position(&self, position: u64) -> Result>; + fn get_accumulator_info(&self) -> AccumulatorInfo; +} + +struct SyncFlexiDagManagerImp { + flexi_dag_storage: Box, + accumulator: MerkleAccumulator, +} + +impl SyncFlexiDagManagerImp { + pub fn new() -> Self { + let flexi_dag_storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::default(), + DBStorage::new( + starcoin_config::temp_dir().as_ref(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + )) + .unwrap(); + let accumulator = MerkleAccumulator::new_empty( + flexi_dag_storage + .get_accumulator_store(starcoin_accumulator::node::AccumulatorStoreType::SyncDag), + ); + SyncFlexiDagManagerImp { + flexi_dag_storage: Box::new(flexi_dag_storage), + accumulator, + } + } + + fn hash_for_hashes(mut hashes: Vec) -> HashValue { + hashes.sort(); + HashValue::sha3_256_of(&hashes.into_iter().fold([].to_vec(), |mut collect, hash| { + collect.extend(hash.into_iter()); + collect + })) + } +} + +impl SyncFlexiDagManager for SyncFlexiDagManagerImp { + fn insert_hashes(&self, mut child_hashes: Vec) -> Result { + child_hashes.sort(); + let accumulator_key = Self::hash_for_hashes(child_hashes.clone()); + self.accumulator.append(&[accumulator_key])?; + self.flexi_dag_storage.put_hashes( + accumulator_key, + SyncFlexiDagSnapshot { + child_hashes, + accumulator_info: self.get_accumulator_info(), + }, + )?; + Ok(accumulator_key) + } + + fn query_by_hash(&self, hash: HashValue) -> Result> { + self.flexi_dag_storage.query_by_hash(hash) + } + + fn fork(&mut self, accumulator_info: AccumulatorInfo) -> Result<()> { + self.accumulator = self.accumulator.fork(Some(accumulator_info)); + Ok(()) + } + + fn get_hash_by_position(&self, position: u64) -> Result> { + self.accumulator.get_leaf(position) + } + + fn get_accumulator_info(&self) -> AccumulatorInfo { + self.accumulator.get_info() + } +} + +#[test] +fn test_syn_dag_accumulator_insert_and_find() { + let syn_accumulator = SyncFlexiDagManagerImp::new(); + let genesis = HashValue::sha3_256_of(b"genesis"); + let b = HashValue::sha3_256_of(b"b"); + let c = HashValue::sha3_256_of(b"c"); + let d = HashValue::sha3_256_of(b"d"); + let e = HashValue::sha3_256_of(b"e"); + let f = HashValue::sha3_256_of(b"f"); + let h = HashValue::sha3_256_of(b"h"); + let i = HashValue::sha3_256_of(b"i"); + let j = HashValue::sha3_256_of(b"j"); + let k = HashValue::sha3_256_of(b"k"); + let l = HashValue::sha3_256_of(b"l"); + let m = HashValue::sha3_256_of(b"m"); + + let genesis_key = syn_accumulator.insert_hashes([genesis].to_vec()).unwrap(); + let layer1 = syn_accumulator + .insert_hashes([b, c, d, e].to_vec()) + .unwrap(); + let layer2 = syn_accumulator + .insert_hashes([f, h, i, k].to_vec()) + .unwrap(); + let layer3 = syn_accumulator + .insert_hashes([j, m, k, l].to_vec()) + .unwrap(); + let layer4 = syn_accumulator.insert_hashes([j, m, l].to_vec()).unwrap(); + + assert_eq!(5, syn_accumulator.get_accumulator_info().get_num_leaves()); + + assert_eq!( + genesis_key, + syn_accumulator.get_hash_by_position(0).unwrap().unwrap() + ); + assert_eq!( + layer1, + syn_accumulator.get_hash_by_position(1).unwrap().unwrap() + ); + assert_eq!( + layer2, + syn_accumulator.get_hash_by_position(2).unwrap().unwrap() + ); + assert_eq!( + layer3, + syn_accumulator.get_hash_by_position(3).unwrap().unwrap() + ); + assert_eq!( + layer4, + syn_accumulator.get_hash_by_position(4).unwrap().unwrap() + ); + + assert_eq!( + [genesis].to_vec(), + syn_accumulator + .query_by_hash(syn_accumulator.get_hash_by_position(0).unwrap().unwrap()) + .unwrap() + .unwrap() + .child_hashes + ); + assert_eq!( + { + let mut v = [b, c, d, e].to_vec(); + v.sort(); + v + }, + syn_accumulator + .query_by_hash(syn_accumulator.get_hash_by_position(1).unwrap().unwrap()) + .unwrap() + .unwrap() + .child_hashes + ); + assert_eq!( + { + let mut v = [f, h, i, k].to_vec(); + v.sort(); + v + }, + syn_accumulator + .query_by_hash(syn_accumulator.get_hash_by_position(2).unwrap().unwrap()) + .unwrap() + .unwrap() + .child_hashes + ); + assert_eq!( + { + let mut v = [j, m, k, l].to_vec(); + v.sort(); + v + }, + syn_accumulator + .query_by_hash(syn_accumulator.get_hash_by_position(3).unwrap().unwrap()) + .unwrap() + .unwrap() + .child_hashes + ); + assert_eq!( + { + let mut v = [j, m, l].to_vec(); + v.sort(); + v + }, + syn_accumulator + .query_by_hash(syn_accumulator.get_hash_by_position(4).unwrap().unwrap()) + .unwrap() + .unwrap() + .child_hashes + ); +} + +#[test] +fn test_syn_dag_accumulator_fork() { + let mut syn_accumulator = SyncFlexiDagManagerImp::new(); + let syn_accumulator_target = SyncFlexiDagManagerImp::new(); + + let genesis = HashValue::sha3_256_of(b"genesis"); + let b = HashValue::sha3_256_of(b"b"); + let c = HashValue::sha3_256_of(b"c"); + let d = HashValue::sha3_256_of(b"d"); + let e = HashValue::sha3_256_of(b"e"); + let f = HashValue::sha3_256_of(b"f"); + let h = HashValue::sha3_256_of(b"h"); + let i = HashValue::sha3_256_of(b"i"); + let j = HashValue::sha3_256_of(b"j"); + let k = HashValue::sha3_256_of(b"k"); + let l = HashValue::sha3_256_of(b"l"); + let m = HashValue::sha3_256_of(b"m"); + let p = HashValue::sha3_256_of(b"p"); + let v = HashValue::sha3_256_of(b"v"); + + let _genesis_key = syn_accumulator.insert_hashes([genesis].to_vec()).unwrap(); + let _genesis_key = syn_accumulator_target + .insert_hashes([genesis].to_vec()) + .unwrap(); + + let layer1 = syn_accumulator + .insert_hashes([b, c, d, e].to_vec()) + .unwrap(); + let layer2 = syn_accumulator + .insert_hashes([f, h, i, k].to_vec()) + .unwrap(); + let layer3 = syn_accumulator + .insert_hashes([j, m, k, l].to_vec()) + .unwrap(); + let layer4 = syn_accumulator.insert_hashes([j, m, l].to_vec()).unwrap(); + + let target_layer1 = syn_accumulator_target + .insert_hashes([b, c, d, e].to_vec()) + .unwrap(); + let target_layer2 = syn_accumulator_target + .insert_hashes([f, h, i, k].to_vec()) + .unwrap(); + let target_layer3 = syn_accumulator_target + .insert_hashes([j, m, k, l].to_vec()) + .unwrap(); + let target_layer4 = syn_accumulator_target + .insert_hashes([p, m, v].to_vec()) + .unwrap(); + let target_layer5 = syn_accumulator_target + .insert_hashes([p, v].to_vec()) + .unwrap(); + + assert_eq!(layer1, target_layer1); + assert_eq!(layer2, target_layer2); + assert_eq!(layer3, target_layer3); + + assert_ne!(layer4, target_layer4); + assert_ne!( + syn_accumulator.get_accumulator_info().get_num_leaves(), + syn_accumulator_target + .get_accumulator_info() + .get_num_leaves() + ); + assert_ne!( + syn_accumulator.get_accumulator_info(), + syn_accumulator_target.get_accumulator_info() + ); + + let info = syn_accumulator_target + .query_by_hash(layer3) + .unwrap() + .unwrap() + .accumulator_info; + + println!("{:?}", info); + assert_eq!( + layer3, + syn_accumulator.get_hash_by_position(3).unwrap().unwrap() + ); + + syn_accumulator.fork(info).unwrap(); + + assert_eq!( + layer3, + syn_accumulator.get_hash_by_position(3).unwrap().unwrap() + ); + + let new_layer4 = syn_accumulator.insert_hashes([p, m, v].to_vec()).unwrap(); + let new_layer5 = syn_accumulator.insert_hashes([p, v].to_vec()).unwrap(); + + assert_eq!(new_layer4, target_layer4); + assert_eq!(new_layer5, target_layer5); + assert_eq!( + syn_accumulator.get_accumulator_info().get_num_leaves(), + syn_accumulator_target + .get_accumulator_info() + .get_num_leaves() + ); + assert_eq!( + syn_accumulator.get_accumulator_info(), + syn_accumulator_target.get_accumulator_info() + ); +} + +#[test] +fn test_accumulator_temp() { + let flexi_dag_storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::default(), + DBStorage::new( + starcoin_config::temp_dir().as_ref(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + )) + .unwrap(); + let mut accumulator = MerkleAccumulator::new_empty( + flexi_dag_storage + .get_accumulator_store(starcoin_accumulator::node::AccumulatorStoreType::SyncDag), + ); + let _hash1 = accumulator.append(&[HashValue::sha3_256_of(b"a")]).unwrap(); + let _hash2 = accumulator.append(&[HashValue::sha3_256_of(b"b")]).unwrap(); + let _hash3 = accumulator.append(&[HashValue::sha3_256_of(b"c")]).unwrap(); + let accumulator_info = accumulator.get_info(); + let _hash4 = accumulator.append(&[HashValue::sha3_256_of(b"d")]).unwrap(); + + assert_eq!( + HashValue::sha3_256_of(b"b"), + accumulator.get_leaf(1).unwrap().unwrap() + ); + accumulator.flush().unwrap(); + accumulator = accumulator.fork(Some(accumulator_info)); + let _hash5 = accumulator.append(&[HashValue::sha3_256_of(b"e")]).unwrap(); + + assert_eq!( + HashValue::sha3_256_of(b"b"), + accumulator.get_leaf(1).unwrap().unwrap() + ); + assert_eq!( + HashValue::sha3_256_of(b"c"), + accumulator.get_leaf(2).unwrap().unwrap() + ); + assert_eq!( + HashValue::sha3_256_of(b"e"), + accumulator.get_leaf(3).unwrap().unwrap() + ); + assert_ne!( + HashValue::sha3_256_of(b"d"), + accumulator.get_leaf(3).unwrap().unwrap() + ); +} diff --git a/sync/src/block_connector/test_write_dag_block_chain.rs b/sync/src/block_connector/test_write_dag_block_chain.rs new file mode 100644 index 0000000000..c74c9aef83 --- /dev/null +++ b/sync/src/block_connector/test_write_dag_block_chain.rs @@ -0,0 +1,215 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::integer_arithmetic)] +use crate::block_connector::test_write_block_chain::create_writeable_block_chain; +use crate::block_connector::WriteBlockChainService; +use starcoin_account_api::AccountInfo; +use starcoin_chain::{BlockChain, ChainReader}; +use starcoin_chain_service::WriteableChainService; +use starcoin_config::NodeConfig; +use starcoin_consensus::{BlockDAG, Consensus, FlexiDagStorage, FlexiDagStorageConfig}; +use starcoin_crypto::HashValue; +use starcoin_genesis::Genesis as StarcoinGenesis; +use starcoin_service_registry::bus::BusService; +use starcoin_service_registry::{RegistryAsyncService, RegistryService}; +use starcoin_storage::Store; +use starcoin_time_service::TimeService; +use starcoin_txpool_mock_service::MockTxPoolService; +use starcoin_types::block::Block; +use starcoin_types::blockhash::ORIGIN; +use starcoin_types::header::Header; +use starcoin_types::startup_info::StartupInfo; +use std::sync::{Arc, Mutex}; + +pub fn gen_dag_blocks( + times: u64, + writeable_block_chain_service: &mut WriteBlockChainService, + time_service: &dyn TimeService, +) -> Option { + let miner_account = AccountInfo::random(); + let mut last_block_hash = None; + if times > 0 { + for i in 0..times { + let block = new_dag_block( + Some(&miner_account), + writeable_block_chain_service, + time_service, + ); + last_block_hash = Some(block.id()); + let e = writeable_block_chain_service.try_connect( + block, + writeable_block_chain_service.get_main().status().tips_hash, + ); + println!("try_connect result: {:?}", e); + assert!(e.is_ok()); + if (i + 1) % 3 == 0 { + writeable_block_chain_service.time_sleep(5); + } + } + } + + let result = writeable_block_chain_service.execute_dag_block_pool(); + let result = result.unwrap(); + match result { + super::write_block_chain::ConnectOk::Duplicate(block) + | super::write_block_chain::ConnectOk::ExeConnectMain(block) + | super::write_block_chain::ConnectOk::ExeConnectBranch(block) + | super::write_block_chain::ConnectOk::Connect(block) => Some(block.header().id()), + super::write_block_chain::ConnectOk::DagConnected + | super::write_block_chain::ConnectOk::MainDuplicate + | super::write_block_chain::ConnectOk::DagPending => { + unreachable!("should not reach here, result: {:?}", result); + } + } +} + +pub fn new_dag_block( + miner_account: Option<&AccountInfo>, + writeable_block_chain_service: &mut WriteBlockChainService, + time_service: &dyn TimeService, +) -> Block { + let miner = match miner_account { + Some(m) => m.clone(), + None => AccountInfo::random(), + }; + let miner_address = *miner.address(); + let block_chain = writeable_block_chain_service.get_main(); + let (block_template, _) = block_chain + .create_block_template(miner_address, None, Vec::new(), vec![], None) + .unwrap(); + block_chain + .consensus() + .create_block(block_template, time_service) + .unwrap() +} + +#[stest::test] +async fn test_dag_block_chain_apply() { + let times = 12; + let (mut writeable_block_chain_service, node_config, _) = create_writeable_block_chain().await; + let net = node_config.net(); + let last_header_id = gen_dag_blocks( + times, + &mut writeable_block_chain_service, + net.time_service().as_ref(), + ); + assert_eq!( + writeable_block_chain_service + .get_main() + .current_header() + .id(), + last_header_id.unwrap() + ); + println!("finish test_block_chain_apply"); +} + +fn gen_fork_dag_block_chain( + fork_number: u64, + node_config: Arc, + times: u64, + writeable_block_chain_service: &mut WriteBlockChainService, +) -> Option { + let miner_account = AccountInfo::random(); + if let Some(block_header) = writeable_block_chain_service + .get_main() + .get_header_by_number(fork_number) + .unwrap() + { + let mut parent_id = block_header.id(); + let net = node_config.net(); + for _i in 0..times { + let block_chain = BlockChain::new( + net.time_service(), + parent_id, + writeable_block_chain_service.get_main().get_storage(), + None, + ) + .unwrap(); + let (block_template, _) = block_chain + .create_block_template(*miner_account.address(), None, Vec::new(), vec![], None) + .unwrap(); + let block = block_chain + .consensus() + .create_block(block_template, net.time_service().as_ref()) + .unwrap(); + parent_id = block.id(); + + writeable_block_chain_service + .try_connect(block, None) + .unwrap(); + } + return Some(parent_id); + } + return None; +} + +#[stest::test(timeout = 120)] +async fn test_block_chain_switch_main() { + let times = 12; + let (mut writeable_block_chain_service, node_config, _) = create_writeable_block_chain().await; + let net = node_config.net(); + let mut last_block = gen_dag_blocks( + times, + &mut writeable_block_chain_service, + net.time_service().as_ref(), + ); + assert_eq!( + writeable_block_chain_service + .get_main() + .current_header() + .id(), + last_block.unwrap() + ); + + last_block = gen_fork_dag_block_chain( + 0, + node_config, + 2 * times, + &mut writeable_block_chain_service, + ); + + assert_eq!( + writeable_block_chain_service + .get_main() + .current_header() + .id(), + last_block.unwrap() + ); +} + +#[stest::test] +async fn test_block_chain_reset() -> anyhow::Result<()> { + let times = 10; + let (mut writeable_block_chain_service, node_config, _) = create_writeable_block_chain().await; + let net = node_config.net(); + let mut last_block = gen_dag_blocks( + times, + &mut writeable_block_chain_service, + net.time_service().as_ref(), + ); + assert_eq!( + writeable_block_chain_service + .get_main() + .current_header() + .id(), + last_block.unwrap() + ); + let block = writeable_block_chain_service + .get_main() + .get_block_by_number(3)? + .unwrap(); + writeable_block_chain_service.reset(block.id(), None)?; + assert_eq!( + writeable_block_chain_service + .get_main() + .current_header() + .number(), + 3 + ); + + assert!(writeable_block_chain_service + .get_main() + .get_block_by_number(2)? + .is_some()); + Ok(()) +} diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 022d2dabc1..61c9f52cfb 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -520,6 +520,60 @@ where } }; } + + fn process_received_block(&self, item: SyncBlockData, next_tips: &mut Option>) -> Result { + + let s = self.collect_item(item, next_tips)?; + ///////// + // let (block, block_info, peer_id) = item.into(); + // let timestamp = block.header().timestamp(); + // let (block_info, action) = match block_info { + // Some(block_info) => { + // //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. + // //So, we just need to update chain and continue + // self.chain.connect(ExecutedBlock { + // block: block.clone(), + // block_info: block_info.clone(), + // })?; + // (block_info, BlockConnectAction::ConnectExecutedBlock) + // } + // None => { + // self.apply_block(block.clone(), peer_id)?; + // self.chain.time_service().adjust(timestamp); + // ( + // self.chain.status().info, + // BlockConnectAction::ConnectNewBlock, + // ) + // } + // }; + + //verify target + let state: Result = + if block_info.block_accumulator_info.num_leaves + == self.target.block_info.block_accumulator_info.num_leaves + { + if block_info != self.target.block_info { + Err(TaskError::BreakError( + RpcVerifyError::new_with_peers( + self.target.peers.clone(), + format!( + "Verify target error, expect target: {:?}, collect target block_info:{:?}", + self.target.block_info, + block_info + ), + ) + .into(), + ) + .into()) + } else { + Ok(CollectorState::Enough) + } + } else { + Ok(CollectorState::Need) + }; + + self.notify_connected_block(block, block_info, action, state?) + } } impl TaskResultCollector for BlockCollector diff --git a/sync/src/tasks/sync_dag_accumulator_task.rs b/sync/src/tasks/sync_dag_accumulator_task.rs new file mode 100644 index 0000000000..b029e4a363 --- /dev/null +++ b/sync/src/tasks/sync_dag_accumulator_task.rs @@ -0,0 +1,169 @@ +use anyhow::{bail, ensure, Chain, Result}; +use bcs_ext::BCSCodec; +use futures::{future::BoxFuture, FutureExt}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_chain::BlockChain; +use starcoin_crypto::HashValue; +use starcoin_logger::prelude::info; +use starcoin_network_rpc_api::dag_protocol::{self, TargetDagAccumulatorLeafDetail}; +use starcoin_storage::{ + flexi_dag::{SyncFlexiDagSnapshot, SyncFlexiDagSnapshotStorage}, + storage::CodecKVStore, +}; +use std::sync::Arc; +use stream_task::{CollectorState, TaskResultCollector, TaskState}; + +use crate::verified_rpc_client::VerifiedRpcClient; + +#[derive(Clone)] +pub struct SyncDagAccumulatorTask { + leaf_index: u64, + batch_size: u64, + target_index: u64, + fetcher: Arc, +} +impl SyncDagAccumulatorTask { + pub fn new( + leaf_index: u64, + batch_size: u64, + target_index: u64, + fetcher: Arc, + ) -> Self { + SyncDagAccumulatorTask { + leaf_index, + batch_size, + target_index, + fetcher, + } + } +} + +impl TaskState for SyncDagAccumulatorTask { + type Item = TargetDagAccumulatorLeafDetail; + + fn new_sub_task(self) -> BoxFuture<'static, Result>> { + async move { + let target_details = match self + .fetcher + .get_accumulator_leaf_detail(dag_protocol::GetTargetDagAccumulatorLeafDetail { + leaf_index: self.leaf_index, + batch_size: self.batch_size, + }) + .await? + { + Some(details) => details, + None => { + bail!("return None when sync accumulator for dag"); + } + }; + Ok(target_details) + } + .boxed() + } + + fn next(&self) -> Option { + //this should never happen, because all node's genesis block should same. + if self.leaf_index == 0 { + // it is genesis + return None; + } + + let next_number = self.leaf_index.saturating_add(self.batch_size); + if next_number > self.target_index - 1 { + // genesis leaf doesn't need synchronization + return None; + } + Some(Self { + fetcher: self.fetcher.clone(), + leaf_index: next_number, + batch_size: self.batch_size, + target_index: self.target_index, + }) + } +} + +pub struct SyncDagAccumulatorCollector { + accumulator: MerkleAccumulator, + accumulator_snapshot: Arc, + target: AccumulatorInfo, + start_leaf_index: u64, +} + +impl SyncDagAccumulatorCollector { + pub fn new( + accumulator: MerkleAccumulator, + accumulator_snapshot: Arc, + target: AccumulatorInfo, + start_leaf_index: u64, + ) -> Self { + Self { + accumulator, + accumulator_snapshot, + target, + start_leaf_index, + } + } +} + +impl TaskResultCollector for SyncDagAccumulatorCollector { + type Output = (u64, MerkleAccumulator); + + fn collect( + &mut self, + mut item: TargetDagAccumulatorLeafDetail, + ) -> anyhow::Result { + let accumulator_leaf = BlockChain::calculate_dag_accumulator_key(item.tips.clone())?; + self.accumulator.append(&[accumulator_leaf])?; + let accumulator_info = self.accumulator.get_info(); + if accumulator_info.accumulator_root != item.accumulator_root { + bail!( + "sync occurs error for the accumulator root differs from other!, local {}, peer {}", + accumulator_info.accumulator_root, + item.accumulator_root + ) + } + self.accumulator.flush()?; + + let num_leaves = accumulator_info.num_leaves; + self.accumulator_snapshot.put( + accumulator_leaf, + SyncFlexiDagSnapshot { + child_hashes: item.tips.clone(), + accumulator_info: accumulator_info.clone(), + }, + )?; + + item.tips.iter().try_fold((), |_, block_id| { + self.accumulator_snapshot.put( + block_id.clone(), + SyncFlexiDagSnapshot { + child_hashes: item.tips.clone(), + accumulator_info: accumulator_info.clone(), + }, + ) + })?; + + if num_leaves == self.target.num_leaves { + Ok(CollectorState::Enough) + } else { + Ok(CollectorState::Need) + } + } + + fn finish(self) -> Result { + let accumulator_info = self.accumulator.get_info(); + + ensure!( + accumulator_info == self.target, + "local accumulator info: {:?}, peer's: {:?}", + accumulator_info, + self.target + ); + info!( + "finish to sync accumulator, its info is: {:?}", + accumulator_info + ); + + Ok((self.start_leaf_index, self.accumulator)) + } +} diff --git a/sync/src/tasks/sync_dag_block_task.rs b/sync/src/tasks/sync_dag_block_task.rs new file mode 100644 index 0000000000..7187f25e23 --- /dev/null +++ b/sync/src/tasks/sync_dag_block_task.rs @@ -0,0 +1,174 @@ +use crate::{tasks::BlockFetcher, verified_rpc_client::VerifiedRpcClient}; +use anyhow::{Ok, Result}; +use futures::{future::BoxFuture, FutureExt}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_chain::BlockChain; +use starcoin_chain_api::{ChainWriter, ExecutedBlock}; +use starcoin_logger::prelude::info; +use starcoin_network_rpc_api::dag_protocol::{GetSyncDagBlockInfo, SyncDagBlockInfo}; +use starcoin_storage::{ + block_info, flexi_dag::SyncFlexiDagSnapshotStorage, storage::CodecKVStore, Store, +}; +use starcoin_types::block::Block; +use std::{collections::HashMap, sync::Arc}; +use stream_task::{CollectorState, TaskResultCollector, TaskState}; + +use super::{block_sync_task::SyncBlockData, BlockLocalStore}; + +#[derive(Clone)] +pub struct SyncDagBlockTask { + accumulator: Arc, + start_index: u64, + target: AccumulatorInfo, + fetcher: Arc, + accumulator_snapshot: Arc, + local_store: Arc, +} +impl SyncDagBlockTask { + pub fn new( + accumulator: MerkleAccumulator, + start_index: u64, + target: AccumulatorInfo, + fetcher: Arc, + accumulator_snapshot: Arc, + local_store: Arc, + ) -> Self { + SyncDagBlockTask { + accumulator: Arc::new(accumulator), + start_index, + target, + fetcher, + accumulator_snapshot: accumulator_snapshot.clone(), + local_store: local_store.clone(), + } + } +} + +impl SyncDagBlockTask { + async fn fetch_absent_dag_block(&self, index: u64) -> Result> { + let leaf = self + .accumulator + .get_leaf(index) + .expect(format!("index: {} must be valid", index).as_str()) + .expect(format!("index: {} should not be None", index).as_str()); + + let snapshot = self + .accumulator_snapshot + .get(leaf) + .expect(format!("index: {} must be valid for getting snapshot", index).as_str()) + .expect(format!("index: {} should not be None for getting snapshot", index).as_str()); + + // let block_with_infos = self + // .local_store + // .get_block_with_info(snapshot.child_hashes.clone())?; + + // assert_eq!(block_with_infos.len(), snapshot.child_hashes.len()); + + // the order must be the same between snapshot.child_hashes and block_with_infos + let mut absent_block = vec![]; + let mut result = vec![]; + snapshot.child_hashes.iter().for_each(|block_id| { + absent_block.push(block_id.clone()); + result.push(SyncDagBlockInfo { + block_id: block_id.clone(), + block: None, + peer_id: None, + dag_parents: vec![], + dag_transaction_header: None, + }); + }); + + let fetched_block_info = self + .fetcher + .fetch_blocks(absent_block) + .await? + .iter() + .map(|(block, peer_info, parents, transaction_header)| { + ( + block.header().id(), + ( + block.clone(), + peer_info.clone(), + parents.clone(), + transaction_header.clone(), + ), + ) + }) + .collect::>(); + + // should return the block in order + result.iter_mut().for_each(|block_info| { + block_info.block = Some( + fetched_block_info + .get(&block_info.block_id) + .expect("the block should be got from peer already") + .0 + .to_owned(), + ); + block_info.peer_id = fetched_block_info + .get(&block_info.block_id) + .expect("the block should be got from peer already") + .1 + .to_owned(); + block_info.dag_parents = fetched_block_info + .get(&block_info.block_id) + .expect("the block should be got from peer already") + .2 + .to_owned() + .expect("dag block should have parents"); + block_info.dag_transaction_header = Some( + fetched_block_info + .get(&block_info.block_id) + .expect("the block should be got from peer already") + .3 + .to_owned() + .expect("dag block should have parents"), + ); + }); + result.sort_by_key(|item| item.block_id); + + let block_info = self + .local_store + .get_block_infos(result.iter().map(|item| item.block_id).collect())?; + + Ok(result + .into_iter() + .zip(block_info) + .map(|(item, block_info)| SyncBlockData { + block: item.block.expect("block should exists"), + info: block_info, + peer_id: item.peer_id, + accumulator_root: Some(snapshot.accumulator_info.get_accumulator_root().clone()), + count_in_leaf: snapshot.child_hashes.len() as u64, + dag_block_headers: Some(item.dag_parents), + dag_transaction_header: Some( + item.dag_transaction_header + .expect("dag transaction header should exists"), + ), + }) + .collect()) + } +} + +impl TaskState for SyncDagBlockTask { + type Item = SyncBlockData; + + fn new_sub_task(self) -> BoxFuture<'static, Result>> { + async move { self.fetch_absent_dag_block(self.start_index).await }.boxed() + } + + fn next(&self) -> Option { + let next_number = self.start_index.saturating_add(1); + if next_number >= self.target.num_leaves { + return None; + } + Some(Self { + accumulator: self.accumulator.clone(), + start_index: next_number, + target: self.target.clone(), + fetcher: self.fetcher.clone(), + accumulator_snapshot: self.accumulator_snapshot.clone(), + local_store: self.local_store.clone(), + }) + } +} diff --git a/sync/src/tasks/sync_dag_full_task.rs b/sync/src/tasks/sync_dag_full_task.rs new file mode 100644 index 0000000000..fd21affaad --- /dev/null +++ b/sync/src/tasks/sync_dag_full_task.rs @@ -0,0 +1,338 @@ +use std::sync::{Arc, Mutex}; + +use anyhow::{anyhow, format_err, Ok}; +use async_std::task::Task; +use futures::{future::BoxFuture, FutureExt}; +use network_api::PeerProvider; +use starcoin_accumulator::{ + accumulator_info::AccumulatorInfo, Accumulator, AccumulatorTreeStore, MerkleAccumulator, +}; +use starcoin_chain::BlockChain; +use starcoin_chain_api::{ChainReader, ChainWriter}; +use starcoin_consensus::BlockDAG; +use starcoin_crypto::HashValue; +use starcoin_executor::VMMetrics; +use starcoin_logger::prelude::{debug, info}; +use starcoin_network::NetworkServiceRef; +use starcoin_service_registry::ServiceRef; +use starcoin_storage::{flexi_dag::SyncFlexiDagSnapshotStorage, storage::CodecKVStore, Store}; +use starcoin_time_service::TimeService; +use stream_task::{ + Generator, TaskError, TaskEventCounterHandle, TaskFuture, TaskGenerator, TaskHandle, +}; + +use crate::{block_connector::BlockConnectorService, verified_rpc_client::VerifiedRpcClient}; + +use super::{ + sync_dag_accumulator_task::{SyncDagAccumulatorCollector, SyncDagAccumulatorTask}, + sync_dag_block_task::SyncDagBlockTask, + sync_find_ancestor_task::{AncestorCollector, FindAncestorTask}, + BlockCollector, BlockConnectedEventHandle, ExtSyncTaskErrorHandle, +}; + +pub async fn find_dag_ancestor_task( + local_accumulator_info: AccumulatorInfo, + target_accumulator_info: AccumulatorInfo, + fetcher: Arc, + accumulator_store: Arc, + accumulator_snapshot: Arc, + event_handle: Arc, +) -> anyhow::Result { + let max_retry_times = 10; // in startcoin, it is in config + let delay_milliseconds_on_error = 100; + + let ext_error_handle = Arc::new(ExtSyncTaskErrorHandle::new(fetcher.clone())); + + // here should compare the dag's node not accumulator leaf node + let sync_task = TaskGenerator::new( + FindAncestorTask::new( + local_accumulator_info.num_leaves - 1, + target_accumulator_info.num_leaves, + fetcher, + ), + 2, + max_retry_times, + delay_milliseconds_on_error, + AncestorCollector::new( + Arc::new(MerkleAccumulator::new_with_info( + local_accumulator_info, + accumulator_store.clone(), + )), + accumulator_snapshot.clone(), + ), + event_handle.clone(), + ext_error_handle.clone(), + ) + .generate(); + let (fut, _handle) = sync_task.with_handle(); + match fut.await { + anyhow::Result::Ok(ancestor) => { + return Ok(ancestor); + } + Err(error) => { + return Err(anyhow!(error)); + } + } +} + +async fn sync_accumulator( + local_accumulator_info: AccumulatorInfo, + target_accumulator_info: AccumulatorInfo, + fetcher: Arc, + accumulator_store: Arc, + accumulator_snapshot: Arc, +) -> anyhow::Result<(u64, MerkleAccumulator)> { + let max_retry_times = 10; // in startcoin, it is in config + let delay_milliseconds_on_error = 100; + + let start_index = local_accumulator_info.get_num_leaves().saturating_sub(1); + + let event_handle = Arc::new(TaskEventCounterHandle::new()); + + let ext_error_handle = Arc::new(ExtSyncTaskErrorHandle::new(fetcher.clone())); + + let sync_task = TaskGenerator::new( + SyncDagAccumulatorTask::new( + start_index.saturating_add(1), + 3, + target_accumulator_info.num_leaves, + fetcher.clone(), + ), + 2, + max_retry_times, + delay_milliseconds_on_error, + SyncDagAccumulatorCollector::new( + MerkleAccumulator::new_with_info(local_accumulator_info, accumulator_store.clone()), + accumulator_snapshot.clone(), + target_accumulator_info, + start_index, + ), + event_handle.clone(), + ext_error_handle, + ) + .generate(); + let (fut, handle) = sync_task.with_handle(); + match fut.await { + anyhow::Result::Ok((start_index, full_accumulator)) => { + return anyhow::Result::Ok((start_index, full_accumulator)); + } + Err(error) => { + return Err(anyhow!(error)); + } + } + + // TODO: we need to talk about this + // .and_then(|sync_accumulator_result, event_handle| { + // let sync_dag_accumulator_task = TaskGenerator::new( + // SyncDagBlockTask::new(), + // 2, + // max_retry_times, + // delay_milliseconds_on_error, + // SyncDagAccumulatorCollector::new(), + // event_handle.clone(), + // ext_error_handle, + // ); + // Ok(sync_dag_accumulator_task) + // }); + // return Ok(async_std::task::block_on(sync)); + // match async_std::task::block_on(sync) { + // std::result::Result::Ok((index, accumulator)) => { + // debug!("sync accumulator success, target accumulator info's leaf count = {}, root hash = {}, begin index = {}", + // accumulator.get_info().get_num_leaves(), accumulator.get_info().get_accumulator_root(), index); + // return Ok((index, accumulator)); + // } + // Err(error) => { + // println!("sync accumulator error: {}", error.to_string()); + // Err(error.into()) + // } + // } +} + +fn get_start_block_id( + accumulator: &MerkleAccumulator, + start_index: u64, + local_store: Arc, +) -> anyhow::Result { + let last_block_id = accumulator + .get_leaf(start_index)? + .expect("last block id should not be None"); + + let mut snapshot = local_store + .query_by_hash(last_block_id)? + .expect("tips should not be None"); + snapshot.child_hashes.sort(); + Ok(snapshot + .child_hashes + .iter() + .last() + .expect("last block id should not be None") + .clone()) +} + +async fn sync_dag_block( + start_index: u64, + accumulator: MerkleAccumulator, + fetcher: Arc, + accumulator_snapshot: Arc, + local_store: Arc, + time_service: Arc, + block_event_handle: H, + network: N, + skip_pow_verify_when_sync: bool, + dag: Arc>, + block_chain_service: ServiceRef, + vm_metrics: Option, +) -> anyhow::Result +where + H: BlockConnectedEventHandle + Sync + 'static, + N: PeerProvider + Clone + 'static, +{ + let max_retry_times = 10; // in startcoin, it is in config + let delay_milliseconds_on_error = 100; + let event_handle = Arc::new(TaskEventCounterHandle::new()); + let ext_error_handle = Arc::new(ExtSyncTaskErrorHandle::new(fetcher.clone())); + + let start_block_id = get_start_block_id(&accumulator, start_index, local_store.clone()) + .map_err(|err| TaskError::BreakError(anyhow!(err))); + let chain = BlockChain::new( + time_service.clone(), + start_block_id?, + local_store.clone(), + vm_metrics, + ) + .map_err(|err| TaskError::BreakError(anyhow!(err))); + + let leaf = accumulator + .get_leaf(start_index) + .expect(format!("index: {} must be valid", start_index).as_str()) + .expect(format!("index: {} should not be None", start_index).as_str()); + + let mut snapshot = accumulator_snapshot + .get(leaf) + .expect(format!("index: {} must be valid for getting snapshot", start_index).as_str()) + .expect( + format!( + "index: {} should not be None for getting snapshot", + start_index + ) + .as_str(), + ); + + snapshot.child_hashes.sort(); + let last_chain_block = snapshot + .child_hashes + .iter() + .last() + .expect("block id should not be None") + .clone(); + + let current_block_info = local_store + .get_block_info(last_chain_block)? + .ok_or_else(|| format_err!("Can not find block info by id: {}", last_chain_block)) + .map_err(|err| TaskError::BreakError(anyhow!(err))); + + let accumulator_info = accumulator.get_info(); + let accumulator_root = accumulator.root_hash(); + let sync_task = TaskGenerator::new( + SyncDagBlockTask::new( + accumulator, + start_index.saturating_add(1), + accumulator_info, + fetcher.clone(), + accumulator_snapshot.clone(), + local_store.clone(), + ), + 2, + max_retry_times, + delay_milliseconds_on_error, + BlockCollector::new_with_handle( + current_block_info?.clone(), + None, + chain?, + block_event_handle.clone(), + network.clone(), + skip_pow_verify_when_sync, + accumulator_root, + Some(dag.clone()), + ), + event_handle.clone(), + ext_error_handle, + ) + .generate(); + let (fut, handle) = sync_task.with_handle(); + match fut.await { + anyhow::Result::Ok(block_chain) => { + return anyhow::Result::Ok(block_chain); + } + Err(error) => { + return Err(anyhow!(error)); + } + }; +} + +pub fn sync_dag_full_task( + local_accumulator_info: AccumulatorInfo, + target_accumulator_info: AccumulatorInfo, + fetcher: Arc, + accumulator_store: Arc, + accumulator_snapshot: Arc, + local_store: Arc, + time_service: Arc, + vm_metrics: Option, + connector_service: ServiceRef, + network: NetworkServiceRef, + skip_pow_verify_when_sync: bool, + dag: Arc>, + block_chain_service: ServiceRef, +) -> anyhow::Result<( + BoxFuture<'static, anyhow::Result>, + TaskHandle, + Arc, +)> { + let event_handle = Arc::new(TaskEventCounterHandle::new()); + let task_event_handle = event_handle.clone(); + let all_fut = async move { + let ancestor = find_dag_ancestor_task( + local_accumulator_info.clone(), + target_accumulator_info.clone(), + fetcher.clone(), + accumulator_store.clone(), + accumulator_snapshot.clone(), + task_event_handle.clone(), + ) + .await + .map_err(|err| TaskError::BreakError(anyhow!(err)))?; + + let (start_index, accumulator) = sync_accumulator( + ancestor, + target_accumulator_info, + fetcher.clone(), + accumulator_store.clone(), + accumulator_snapshot.clone(), + ) + .await + .map_err(|err| TaskError::BreakError(anyhow!(err)))?; + + let block_chain = sync_dag_block( + start_index, + accumulator, + fetcher.clone(), + accumulator_snapshot.clone(), + local_store.clone(), + time_service.clone(), + connector_service.clone(), + network, + skip_pow_verify_when_sync, + dag.clone(), + block_chain_service.clone(), + vm_metrics, + ) + .await + .map_err(|err| TaskError::BreakError(anyhow!(err)))?; + return anyhow::Result::Ok(block_chain); + }; + + let task = TaskFuture::new(all_fut.boxed()); + let (fut, handle) = task.with_handle(); + Ok((fut, handle, event_handle)) +} diff --git a/sync/src/tasks/sync_dag_protocol_trait.rs b/sync/src/tasks/sync_dag_protocol_trait.rs new file mode 100644 index 0000000000..78b2093c7a --- /dev/null +++ b/sync/src/tasks/sync_dag_protocol_trait.rs @@ -0,0 +1,29 @@ +use anyhow::Result; +use futures::future::BoxFuture; +use network_p2p_core::PeerId; +use starcoin_network_rpc_api::dag_protocol::{ + SyncDagBlockInfo, TargetDagAccumulatorLeaf, TargetDagAccumulatorLeafDetail, +}; + +pub trait PeerSynDagAccumulator: Send + Sync { + fn get_sync_dag_asccumulator_leaves( + &self, + peer_id: Option, + leaf_index: u64, + batch_size: u64, + ) -> BoxFuture>>; + + fn get_accumulator_leaf_detail( + &self, + peer_id: Option, + leaf_index: u64, + batch_size: u64, + ) -> BoxFuture>>>; + + fn get_dag_block_info( + &self, + peer: Option, + leaf_index: u64, + batch_size: u64, + ) -> BoxFuture>>>; +} diff --git a/sync/src/tasks/sync_find_ancestor_task.rs b/sync/src/tasks/sync_find_ancestor_task.rs new file mode 100644 index 0000000000..5206c2ef0c --- /dev/null +++ b/sync/src/tasks/sync_find_ancestor_task.rs @@ -0,0 +1,115 @@ +use anyhow::{format_err, Result}; +use futures::{future::BoxFuture, FutureExt}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_network_rpc_api::dag_protocol::{self, TargetDagAccumulatorLeaf}; +use starcoin_storage::{flexi_dag::SyncFlexiDagSnapshotStorage, storage::CodecKVStore}; +use std::sync::Arc; +use stream_task::{CollectorState, TaskResultCollector, TaskState}; + +use crate::verified_rpc_client::VerifiedRpcClient; + +#[derive(Clone)] +pub struct FindAncestorTask { + start_leaf_number: u64, + fetcher: Arc, + batch_size: u64, +} +impl FindAncestorTask { + pub(crate) fn new( + current_leaf_numeber: u64, + target_leaf_numeber: u64, + fetcher: Arc, + ) -> Self { + FindAncestorTask { + start_leaf_number: std::cmp::min(current_leaf_numeber, target_leaf_numeber), + fetcher, + batch_size: 3, + } + } +} + +impl TaskState for FindAncestorTask { + type Item = TargetDagAccumulatorLeaf; + + fn new_sub_task(self) -> BoxFuture<'static, Result>> { + async move { + let target_accumulator_leaves = self + .fetcher + .get_dag_accumulator_leaves(dag_protocol::GetDagAccumulatorLeaves { + accumulator_leaf_index: self.start_leaf_number, + batch_size: self.batch_size, + }) + .await?; + Ok(target_accumulator_leaves) + } + .boxed() + } + + fn next(&self) -> Option { + //this should never happen, because all node's genesis block should same. + if self.start_leaf_number == 0 { + return None; + } + + let next_number = self.start_leaf_number.saturating_sub(self.batch_size); + Some(Self { + start_leaf_number: next_number, + batch_size: self.batch_size, + fetcher: self.fetcher.clone(), + }) + } +} + +pub struct AncestorCollector { + accumulator: Arc, + ancestor: Option, + accumulator_snapshot: Arc, +} + +impl AncestorCollector { + pub fn new( + accumulator: Arc, + accumulator_snapshot: Arc, + ) -> Self { + Self { + accumulator, + ancestor: None, + accumulator_snapshot, + } + } +} + +impl TaskResultCollector for AncestorCollector { + type Output = AccumulatorInfo; + + fn collect(&mut self, item: TargetDagAccumulatorLeaf) -> anyhow::Result { + if self.ancestor.is_some() { + return Ok(CollectorState::Enough); + } + + let accumulator_leaf = self.accumulator.get_leaf(item.leaf_index)?.ok_or_else(|| { + format_err!( + "Cannot find accumulator leaf by number: {}", + item.leaf_index + ) + })?; + + let accumulator_info = match self.accumulator_snapshot.get(accumulator_leaf)? { + Some(snapshot) => snapshot.accumulator_info, + None => panic!("failed to get the snapshot, it is none."), + }; + + if item.accumulator_root == accumulator_info.accumulator_root { + self.ancestor = Some(accumulator_info); + return anyhow::Result::Ok(CollectorState::Enough); + } else { + Ok(CollectorState::Need) + } + } + + fn finish(mut self) -> Result { + self.ancestor + .take() + .ok_or_else(|| format_err!("Unexpect state, collector finished by ancestor is None")) + } +} diff --git a/types/src/blockhash.rs b/types/src/blockhash.rs new file mode 100644 index 0000000000..f283d0f387 --- /dev/null +++ b/types/src/blockhash.rs @@ -0,0 +1,71 @@ +use starcoin_crypto::hash::HashValue; +use std::collections::{HashMap, HashSet}; + +pub const BLOCK_VERSION: u16 = 1; + +pub const HASH_LENGTH: usize = HashValue::LENGTH; + +use std::sync::Arc; + +pub type BlockHashes = Arc>; + +/// `blockhash::NONE` is a hash which is used in rare cases as the `None` block hash +pub const NONE: [u8; HASH_LENGTH] = [0u8; HASH_LENGTH]; + +/// `blockhash::VIRTUAL` is a special hash representing the `virtual` block. +pub const VIRTUAL: [u8; HASH_LENGTH] = [0xff; HASH_LENGTH]; + +/// `blockhash::ORIGIN` is a special hash representing a `virtual genesis` block. +/// It serves as a special local block which all locally-known +/// blocks are in its future. +pub const ORIGIN: [u8; HASH_LENGTH] = [0xfe; HASH_LENGTH]; + +pub trait BlockHashExtensions { + fn is_none(&self) -> bool; + fn is_virtual(&self) -> bool; + fn is_origin(&self) -> bool; +} + +impl BlockHashExtensions for HashValue { + fn is_none(&self) -> bool { + self.eq(&HashValue::new(NONE)) + } + + fn is_virtual(&self) -> bool { + self.eq(&HashValue::new(VIRTUAL)) + } + + fn is_origin(&self) -> bool { + self.eq(&HashValue::new(ORIGIN)) + } +} + +/// Generates a unique block hash for each call to this function. +/// To be used for test purposes only. +pub fn new_unique() -> HashValue { + use std::sync::atomic::{AtomicU64, Ordering}; + static COUNTER: AtomicU64 = AtomicU64::new(1); + let c = COUNTER.fetch_add(1, Ordering::Relaxed); + HashValue::from_u64(c) +} + +/// TODO:FIXME as u256 +pub type BlueWorkType = u128; + +/// The type used to represent the GHOSTDAG K parameter +pub type KType = u16; + +/// Map from Block hash to K type +pub type HashKTypeMap = std::sync::Arc>; + +pub type BlockHashMap = HashMap; + +/// Same as `BlockHashMap` but a `HashSet`. +pub type BlockHashSet = HashSet; + +pub struct ChainPath { + pub added: Vec, + pub removed: Vec, +} + +pub type BlockLevel = u8; diff --git a/types/src/header.rs b/types/src/header.rs new file mode 100644 index 0000000000..8c5dcb591b --- /dev/null +++ b/types/src/header.rs @@ -0,0 +1,60 @@ +use crate::block::BlockHeader; +use crate::blockhash::{BlockLevel, ORIGIN}; +use crate::U256; +use serde::{Deserialize, Serialize}; +use starcoin_crypto::HashValue as Hash; +use std::sync::Arc; + +pub trait ConsensusHeader { + fn parents_hash(&self) -> &[Hash]; + fn difficulty(&self) -> U256; + fn hash(&self) -> Hash; + fn timestamp(&self) -> u64; +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct Header { + block_header: BlockHeader, + parents_hash: Vec, +} + +impl Header { + pub fn new(block_header: BlockHeader, parents_hash: Vec) -> Self { + Self { + block_header, + parents_hash, + } + } + + pub fn genesis_hash(&self) -> Hash { + Hash::new(ORIGIN) + } +} + +impl ConsensusHeader for Header { + fn parents_hash(&self) -> &[Hash] { + &self.parents_hash + } + fn difficulty(&self) -> U256 { + self.block_header.difficulty() + } + fn hash(&self) -> Hash { + self.block_header.id() + } + + fn timestamp(&self) -> u64 { + self.block_header.timestamp() + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct HeaderWithBlockLevel { + pub header: Arc

, + pub block_level: BlockLevel, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] +pub struct CompactHeaderData { + pub timestamp: u64, + pub difficulty: U256, +} diff --git a/vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv b/vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv new file mode 100644 index 0000000000000000000000000000000000000000..2aa602ac288885c085b6f24d688ec4ea3d5c4b81 GIT binary patch literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv new file mode 100644 index 0000000000000000000000000000000000000000..2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05 GIT binary patch literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv new file mode 100644 index 0000000000000000000000000000000000000000..7d3359f06d3ca7172b76018eb71bb97aadf699d0 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv new file mode 100644 index 0000000000000000000000000000000000000000..b8584e7754d63d0f975320ee01be7b17fc125d7a GIT binary patch literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv b/vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv new file mode 100644 index 0000000000000000000000000000000000000000..297fc8eb9bf27b993901390d58ac6b21e2ea943c GIT binary patch literal 538 zcmYk2&x+JQ5XP&j|0P{L<3yGPVJ`!+i&rm>ih2;igWy5$Ax=7L*i15XdRCvqqu@aw z!n61s;tTi^)`)_A_|f%!d{yv$KKsQM02Tey+`nJMQoaix0H2U+BbtCC}f8 z!guwvcX^pBvclSI?8B+5?Q`4yDlbvIt@eI=6)LQ+GiTu+_drr6|I#`pc4=eo%@#Wu#B&&^d7UpBfwjIHXXRA$*x57YLF$LU=?$L%z> z^Qs@Oo1t&(CS4wOamy@52)y5{DGSL*X$~oO^r>4mepF{ECQKR vwv6jQO!}5Nb5OYp*6hyp+T3W4lai(WxkC|EAq)3SpDe3@piQz>+fa zo`-xuJ|JI_Z*Zl`Re4SRATFo!3F#gjghZ!epsKIwYr3a*XaBPBwec8Z5oZM`ycF$! zf&5A=seg+vh5uLoD;@l?RPg>>+SGq9{fV1@lxOXK%Ky~S-!3fYzPYe~mYe|-Oftnh z=Ci9d=2^JH`D?2x^E*0FcaVZ6Hw2v!KJ zp1%OT&+aWQq0jpqWBTj?FC$aun6b~+cn+Dyc}`4oky2jI8-yDd-TG#p%JRK@gz#Zy z8R6C?)RpY}mmPYv=+H-3$nx=3a^roTFER0|TxRf7USjfRH#qnFpYvPTtzX~{{a@an z&ngTdLlw-Km0S4dWqW zL`WnE8^qi*9)v8!AR&f#GJh=cQG|LU1-DL4#scS&J9m8OED+qY98Z#ml;VOL-^IW< zoNJJt4TK9ea-o#(%!g9Y_!k5ac{b$e!~`xoOoJFr-i9VpD6&GA<8&SS{tieq+EgIW z$Iuj=44sCQAt%;15h8RBTq8S5%jFtEI1Cl?QPyIF{b6!I&VU<*9)hvziH^(JL$881 zgmIiAcLBw)8KX^d3wFFkvhswh!Pvwti5DNg^dIf+_71yQ^tjhg9<|&3WH3ku-gdv= z>kriSQPR!MJWaaEARX+a``x7fxOLEaoxV-8V{a(%UM0I(ub=zqu-`pd42gB-!Sf_* zwOd&$_dM-pQyRoN4IlG(*4^tF#4nEz5~7P!^y6OVpp`{al2@yR&kxXNa^kMsYPaLp z7&P9UWawla3^OI0A-zLqJS8=+d${9w@#|jucy5Z0JBL~PF3sMMt|a%7i!m0RX(xlM z-#h*=@BMVWmsS7g@ixo5cgy0?H-YPqO%RYmhh#L)>mZ zX<;KgqSJQZ5LVJ@W>{9Isnc$nrLDK=9}<%e;(oH94zi>lP0>laKv~3?>TW+h$kJX{ zJ3-oM?I-yYGN!JEj_~xOfy)=2-hO{kc9;oZewM;wcnq;QVjXye*;pC)- zlanZE*8EX(ByCJm-Hj_xLJyvSagT>jTHW^B#JIT|$~Dd~J*eW|Ub&zA{xC&mYMAp+ zw_j}UJllyMJ$drk_RbEDR+`1mRJkdeZQ(<%pT+yb2U|RH7vK=M^+MV`!Vzi5t^WRD z2Qw+qF>?;$H!hnJP1o3bIF)ptnfh@Nycw|~NGH^9C)D1AN+#6)h+@E>aD0Dk3jqFc zU8hwpdBch;7T?YrO!`XuqPSW$u!%k!c%k5o>uV*$*M-FZR`BAz z4Jix7dz-S(jcBR{H-NXrVYGF8*9K!6>lTh_;Gl{28Zi8q1Q_%U+}EsFrxb2N8NiYf zWeR{-V7Iq5QGwi;#=9Y12zgGkHCaI*2tWlbRHf-bVQjCUNF;|6A!rCFyCq5=Dnw%0 z3SI?f#97KGcsG-QzKvn9R8q?g?n))GR9bFwtBf?tU?q%bNSMdM z%I~PH;4Eqf`fk$|)=)3WjgbvywOYdyfVK8*q@y*jg6$aoW_48eW}FOf7se{htf#^H zTG+ry8yXK2!n=$Ko5ETX)$tC6zR`0tXn4k2Z8V&t?%umj%|wA+3#J3udNAW;R%`u# z0vKcKA?9%sz`z0(&@+tsZ8-M(JPBrVtl=+7r2?N78`T^qE>Z=@(dJPZQ2D}lP$s`l zc@c~9AToG74Or}JBe06TqTCN#BaCW>u!Jnvc-&S_*0@GxGzgm#F1kjPBi9J8p)FGe zOB1f?ie-kuGF6K!suorXE|Gc|;wWrr8KP9L8B|m`XJdypm#1u^3S2=pSX(HrTcKT} z8`!!WrneP_O^zpvMY3UWNp4|gVUulP;IIi>09Uaf&BcaTl2V0uN%uLH4;=wHYD8A# zu(w<20Uez(u3}tPirn$VJxotJf5@PQ%ORJ=dk^)xhHJ>lDdm&1ZumnE*KyfQErS~c zMNbX>5a0$i4kvisgPTsXJQ*6&w0NL>rz|&J`*31%fZsuwfx%})9cHjyysl>8HWpZ$ zspr#FBp~BP9`0ZWy&h0ctaDe7X5cOggN-F@OeoLero#2mdRAIxVOaU%3J!w85pj*T zK6b$0k_cr}Z)vrNQz3nE&7aoRh+z5rBfIv$HqX>vuBakgh$%XR> zSOx>ip$HA3M&-oODPrdo?}3PvvaSRPEsRhKSpbqKN+9GCc3KJl6!ZHsKIfF?_93m> zI!FCjcMG1xrcarhv~KF2KVp8HZ@F&UxPIYfT)*}Uo~4}ArfgR2bhhndS9FW|dU|zx zCx`quIR-$R1MoNo4@#B?JMt>f?l<-pw1Y7Q(&#v7V?E%2Jf11|1qiq*5dZ)H literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv new file mode 100644 index 0000000000000000000000000000000000000000..9b6c88aad4ff46335891d5a6b5f3c1745f20396d GIT binary patch literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv new file mode 100644 index 0000000000000000000000000000000000000000..44d5f27272aecd2f94a15d3e82260ffe92ab259a GIT binary patch literal 1493 zcmY*ZOLE&r5bf?6V1OAAACaO&N{*F}EuTP!E$35CB~@M|StMDrAQ6;NrT`WQ+0iAm zNRMiyD+2-#$lZ7MwgNIG0h_jLDr{icbTcgKGj7XStbn)I3Y;unfMOe=1G|C?NJ@gD)_8(J5`4$C#weze5OgJjt|aoc4JM`q>;cVqaJ`P>2;#kH7>%NFgAa?q0EUP0NyqM=@Q!I+S{`Tu1+^_1os@KB0->sUWD&6mkt1_?s=M`t(*m!n% znXj6lGcyL)xB8;Z&dNm-?%dh+O%wmPnrBV^Wwy#Lie=GkORJk^FwfUbUER*|>s50* z{NJwQRow4Ixu}Axys_l7f;ZteMcM2KovS)48?RkwP30HsStm`gSG_6M(k`aXo140v zU1XQ9!sqPm2;4ezjtjc^)R?Td6bFnPeFY~!&U02t+%71F= z?6R?YTa{$XI?v{}yUJj5Ae(l(v3T|%_7Ku?$VIVSZp&HpcOgM<_NDfHiH3^8A6z=4J&O5sFm11Ua%&%p@{-$XU^a%>|+6U;dKp&y#aAm`(1k7?~zWF#GcZWY6|7{WgFbI~}FZfp*@e2;+Z}Fm?!<5V@O8{rY&kmVoO5hPi#c_WC{PAeal;XWRdiD$=;BkuVV!llLzdy~Pz(2R!T;ba1+5hWUdMxtgSu^HnT zLypK3%QNyii{!}YGR-vH;#1%zp(?F3Qc?V)RuO@+BTC6df{;$lnB-h(u9YDGk??3V z>cpxJA}N(ZinIw5Brf?rlh|9I#d>6S6}V4hib8hHWp7;NHh%N=>vz4^b?aU&7OmTC z+@^Q2a8>Y2?^?=DFJ1fH+R^IFZ$j(e--Np5!K;%`?U|d;cCBoUM)lJsZbGRB+x6j4`jab!53@)gG>D;FR9!uM0{k?xYg|W{Z%>o0YyEO(7B^+N%Sq09VaiTS-qv-np$^45-`q6KdMACgaNqOm zqUry?&%vAXx93+ESNW^6vv1F@u54`Yu4f%3E{aeb6m)QH$nQiI`?H)>?qgmOCjaQe zhuoLN%IVgv=pbF2xv~knF5T+{br|YElAoVVd8TD>Y$E_>tt;?_L zvMKhbX7{4=h9oX&BtHFN#wO#dP_);zukK$=#tvuiKDd+3ZRX@w#R3&hVi7kZBwdL5%e0IAyc~`oLih1ft=bWi*Tm3|BZZR`zk4u`${tqwB&jlNdQi zD-0Mh<}W$pv7D%gfH6VD{K0J0w-k91M+!1NfRWB4(06zeUCm!HG@YpwykSahRAzpYyV$RwwX(GDPj=!C6X3m^5<1=3^{9$VdArqrj zvbsV)e2$eb$(;Hf`3CF1(-*q&gY9TP*`KiQ+1CUNF(QZ}1*u3wIx>)nEMy~w3aE&t zkb_)QG7KFR3Od583UbdgjIi^Ll$3%ua^IC0osf81iWwp6$;{{6mtrQd5IRJ zI0b?%f{D5Wf-Qq6o(5q`q%Er$8D}&dDQ69gb$bP2qU+i@X}ShE>dQ8mN`&9AAjX@x zfULKQQ&@4|b}?4y1q9f+cw!nWg-f$#q^^Snym;OJ%rtf#JE4Y}UJ zDXf%M7C~XXzXY2xD&b;aJjekTc9yb&g%pQU;=4C6tRsaLykcwp7y zZkLCdh$F2QM{Ut5)Z&C!TdjnrDNn5%JmRTH?_O_~$FLzf5l?DCKiCspkqxJA2T^Ab zbohOqq!5RKty*uldE(Tg+I}FSQQ)m0*6K+Rb~$Y{YYg6djjfTwY>FOFv!K^koACjU zjAp`vbdU_^C*Xm|_FGBt1cK}&aX(Ij?p!PO2cxa|?IiB`k1t*FvlxEXE61pfoY_21 zZ05w33&%Xhg#QFq_PgUP`j1C@JRavt9>-hCf&=b%MZ|+7ca=%|;mgA+|4+sIb7rTapt&S3KiJ!aE|( zctRuoq|_hmbw#@G1NOtbL9~8%&=Jx6Bx5-8sGa%Qu+L$IHWXmpIocy7{xpNqXMUPD zk35i43o4$(!$Ydd?fl#(wLa8hH?G~i*4S?N)vc}FYmJ7M@D}I2MM(ZNXYG!{$=&;18?p~ZEu{@`j z|EZB?8ru0~rjzyDglB^!I%I7};GA+e=2p5a(sb~;PSQdMnsK5t<1qd$8V+1wEQcKX4^iy!qGJK^K-VW*YdyeaNC ztJNTR*5re|gN=um#e-pQryAe7+7`RlKi#?W2<~B+05)=Mf+Z|k$Yt;r;FWmFhH;{G z3whH_16fjlTvA}_vPpn2 zra?BVAUCxv=J~t^>9j@wvO8j)y(K8j;S08eWS$^dkdZ8+*7yoe$8D8>Vxy8Sh-B*o zayBM3V;ck}#ja*k0@OxZ$yJg9Qb<{lUrT2|Eiyv1%sLr?^v38Ifz0MO0a@sNKuSy)`q7bUTmy^tCyp6GCqGx;>9Em(2wrUs*)%?FA(7 zm`h;Umk5k}6=hQPWddVgLoZ`Da60~VB;=-r+%B1i=gpp{Z;gty=@kd-dQUm zy}OQ%5(gNzGW!j3g@9?7&}wEZoy$$-W^%K+d~Pl`KXN5k%5CMY=HA-a9$8aOSymK9 zHYD+rWbr65l?V|_F+U(UgH@sM-^&-ir2VxoDX)X36H}LTLS>>WG$DwJM8eeYmGH$Z zRIHXIxEC@qMf$^pQ7A!7f_602N&=Ax5~?Fb0}?W_s+tIoB$L>OaHAoY1cjjIBuI&P zko$`$CW}?oJRgNHQ4J)7=tNR=M49L!N?lUoPz_0BM3ZRT#H=_1Xt@ zW7lssc`fq%wz})LkG)13hmg0!ZrGc%^LZr-+=w^MQoDY@ck16>@R`s9q zc4EKd*G}jiH>}i7c)i=?Mn91Ix2qF>UPft;yA%>R2VJH@zqCcN&3P=apLEb)qm?^;$fP+*T(MhhkL& zw;j51Vz)WZU6>wqpESMjr04eHhA`oGqh8v2HQ;X84bG+q%m-d{QV-l`d#-;J_#HoV zoBG3hJ!$nJV(16jLmq^fOseiXUfYX2x9NSzjUhWZWSwI_aJneuL>PFOy0CM)^}_7x z_`CexgaO5g*7oZm*$en?G|-d&!P;dVo4&trquCw!tGO?6->(J@cK;i5qey z^jkdN?bLGsNaE}cf`&je#<$%h5$LI z*L%1$-FBmkr)E%;saM2HrC7eSH5OuVBJ6bsYl67DLB#9XfH$$uH%Aj?W7K)(VI5ELrNNfCsO2wOq@u(=zOOkS@8ztgu z;GN^*ga4%S_G9mh&wu~%54~mo)AAqH)VHNSz>og^&p#QB=fCxS^Z2hVL%O!@b&kWg zH#gsFRgP+>wMUJ5boZ|Ju)4G31|L>=_sP@kM_0WM&RR!1{=2u1y$83yb9C>qFn-b7 zoA|^}2ut9_hcXqZ!88MmEYp$KSQ6<3%`pSVNjgQR=?tBvc{)dJI!_nqB3+`F7{%QJ zGm)A!MJ<}98OrDg9i?M*oMu_d0A{f?%a}!!!JASUFIsF=T-M^JOxQSXNGyv~rkCk5 zU7@RN!Ymn3maJk~Lh(sM*%~Me#wF!jsS;JnvU$ZSvT1sytQd*cNHtI@LDh1?+_Z{x zvn&!Vl`&CfTgkF+E6qdLyl*b?qvGS^XUon1zL*}w~< z+URGX5w>E0I>?8uqC|lLTcbr*#A}HH%ReS;U6B(ByeIXZWws&kz;KdGMXEE+DjH)< zM?S%lNCk2Rj)?#3v1b%_SD1;^p!8MjSqihTaF?(wa&}yS0?t8H09RhD0B&G01^Nt8 z61cNTtAN-`uR-()N=xM=;#ol4HxYOFHBu4UF=7~HtP(|li*&7QS_QgMPFb2j{<{SI zEST%oI=0&)!n=eP7tB>_m8v*jjY|;&i7i%?#Kn2@TG=uQzEX@KUe?c{SG7L87|bDg z?{`;={W)yGXPKMVf4MCU7jABS_H*}>LDldZ;1?ggBX%`O{7U~C?i}@%)Gr21X1T|y zx&F%5K)XK&?6JPc=W?I1@AsKO5GwFr&5E@W=sG1-I&Y8vkvR_w@bg7HLpzW9PSx{p zIuo%7l$7DxzyQM!A%t7T-nF)3mjcs#1;WH2M106V3bP#vSxoe9FaN aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..fe06059a19298b989fe5ef630ff59c63111237cd GIT binary patch literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..f2d215e295f47705bf56c11f22c759c52fc43203 GIT binary patch literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv new file mode 100644 index 0000000000000000000000000000000000000000..7b0364a1abd25488c88f8e031c2c94f58b93d5bf GIT binary patch literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..e9736e40d3098473a75e9cd4f6390736cb29f34c GIT binary patch literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..9fc3054e32464ca19f39e147e1cd114bdb85369c GIT binary patch literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv new file mode 100644 index 0000000000000000000000000000000000000000..a5eebc7a853d9d71538903b6e37234f5bf3caccd GIT binary patch literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv b/vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv new file mode 100644 index 0000000000000000000000000000000000000000..e37f2baf06e884792c42fdb4bf03bae888c3fe3f GIT binary patch literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv new file mode 100644 index 0000000000000000000000000000000000000000..aa92ddbcab19b8289c18c5d75dd714715d8e0637 GIT binary patch literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv new file mode 100644 index 0000000000000000000000000000000000000000..5fda8be8e329a6340402ae254ff9ad5ca5502fe0 GIT binary patch literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv new file mode 100644 index 0000000000000000000000000000000000000000..1f79c3cc854a84bffd1177410c4789f649e19618 GIT binary patch literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/084_String.mv b/vm/stdlib/compiled/12/11-12/stdlib/084_String.mv new file mode 100644 index 0000000000000000000000000000000000000000..a718cd408f0896e17de2b6da730b431dfe2a7a7c GIT binary patch literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv b/vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv new file mode 100644 index 0000000000000000000000000000000000000000..297bb011c407ad2b2ebe612110e13206bca96db8 GIT binary patch literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv new file mode 100644 index 0000000000000000000000000000000000000000..6f1c313bfae799fc73233ce143be0b2bccc22e96 GIT binary patch literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..dca83ebaca3cf7db7eab5177d516a9c39ff491cb GIT binary patch literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..23b7501a57b223bcba74253c5b6edff659f4d604 GIT binary patch literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv b/vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv new file mode 100644 index 0000000000000000000000000000000000000000..e923809b5f9f07f184e1012756e5235bafd57552 GIT binary patch literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv new file mode 100644 index 0000000000000000000000000000000000000000..e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249 GIT binary patch literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/052_Epoch.mv b/vm/stdlib/compiled/12/stdlib/052_Epoch.mv new file mode 100644 index 0000000000000000000000000000000000000000..2aa602ac288885c085b6f24d688ec4ea3d5c4b81 GIT binary patch literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv new file mode 100644 index 0000000000000000000000000000000000000000..2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05 GIT binary patch literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv new file mode 100644 index 0000000000000000000000000000000000000000..7d3359f06d3ca7172b76018eb71bb97aadf699d0 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv new file mode 100644 index 0000000000000000000000000000000000000000..b8584e7754d63d0f975320ee01be7b17fc125d7a GIT binary patch literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/060_Offer.mv b/vm/stdlib/compiled/12/stdlib/060_Offer.mv new file mode 100644 index 0000000000000000000000000000000000000000..297fc8eb9bf27b993901390d58ac6b21e2ea943c GIT binary patch literal 538 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 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv new file mode 100644 index 0000000000000000000000000000000000000000..9b6c88aad4ff46335891d5a6b5f3c1745f20396d GIT binary patch literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv new file mode 100644 index 0000000000000000000000000000000000000000..44d5f27272aecd2f94a15d3e82260ffe92ab259a GIT binary patch literal 1493 zcmY*ZOLE&r5bf?6V1OAAACaO&N{*F}EuTP!E$35CB~@M|StMDrAQ6;NrT`WQ+0iAm zNRMiyD+2-#$lZ7MwgNIG0h_jLDr{icbTcgKGj7XStbn)I3Y;unfMOe=1G|C?NJ@gD)_8(J5`4$C#weze5OgJjt|aoc4JM`q>;cVqaJ`P>2;#kH7>%NFgAa?q0EUP0NyqM=@Q!I+S{`Tu1+^_1os@KB0->sUWD&6mkt1_?s=M`t(*m!n% znXj6lGcyL)xB8;Z&dNm-?%dh+O%wmPnrBV^Wwy#Lie=GkORJk^FwfUbUER*|>s50* z{NJwQRow4Ixu}Axys_l7f;ZteMcM2KovS)48?RkwP30HsStm`gSG_6M(k`aXo140v zU1XQ9!sqPm2;4ezjtjc^)R?Td6bFnPeFY~!&U02t+%71F= z?6R?YTa{$XI?v{}yUJj5Ae(l(v3T|%_7Ku?$VIVSZp&HpcOgM<_NDfHiH3^8A6z=4J&O5sFm11Ua%&%p@{-$XU^a%>|+6U;dKp&y#aAm`(1k7?~zWF#GcZWY6|7{WgFbI~}FZfp*@e2;+Z}Fm?!<5V@O8{rY&kmVoO5hPi#c_WC{PAeal;XWRdiD$=;BkuVV!llLzdy~Pz(2R!T;ba1+5hWUdMxtgSu^HnT zLypK3%QNyii{!}YGR-vH;#1%zp(?F3Qc?V)RuO@+BTC6df{;$lnB-h(u9YDGk??3V z>cpxJA}N(ZinIw5Brf?rlh|9I#d>6S6}V4hib8hHWp7;NHh%N=>vz4^b?aU&7OmTC z+@^Q2a8>Y2?^?=DFJ1fH+R^IFZ$j(e--Np5!K;%`?U|d;cCBoUM)lJsZbGRB+x6j4`jab!53@)gG>D;FR9!uM0{k?xYg|W{Z%>o0YyEO(7B^+N%Sq09VaiTS-qv-np$^45-`q6KdMACgaNqOm zqUry?&%vAXx93+ESNW^6vv1F@u54`Yu4f%3E{aeb6m)QH$nQiI`?H)>?qgmOCjaQe zhuoLN%IVgv=pbF2xv~knF5T+{br|YElAoVVd8TD>Y$E_>tt;?_L zvMKhbX7{4=h9oX&BtHFN#wO#dP_);zukK$=#tvuiKDd+3ZRX@w#R3&hVi7kZBwdL5%e0IAyc~`oLih1ft=bWi*Tm3|BZZR`zk4u`${tqwB&jlNdQi zD-0Mh<}W$pv7D%gfH6VD{K0J0w-k91M+!1NfRWB4(06zeUCm!HG@YpwykSahRAzpYyV$RwwX(GDPj=!C6X3m^5<1=3^{9$VdArqrj zvbsV)e2$eb$(;Hf`3CF1(-*q&gY9TP*`KiQ+1CUNF(QZ}1*u3wIx>)nEMy~w3aE&t zkb_)QG7KFR3Od583UbdgjIi^Ll$3%ua^IC0osf81iWwp6$;{{6mtrQd5IRJ zI0b?%f{D5Wf-Qq6o(5q`q%Er$8D}&dDQ69gb$bP2qU+i@X}ShE>dQ8mN`&9AAjX@x zfULKQQ&@4|b}?4y1q9f+cw!nWg-f$#q^^Snym;OJ%rtf#JE4Y}UJ zDXf%M7C~XXzXY2xD&b;aJjekTc9yb&g%pQU;=4C6tRsaLykcwp7y zZkLCdh$F2QM{Ut5)Z&C!TdjnrDNn5%JmRTH?_O_~$FLzf5l?DCKiCspkqxJA2T^Ab zbohOqq!5RKty*uldE(Tg+I}FSQQ)m0*6K+Rb~$Y{YYg6djjfTwY>FOFv!K^koACjU zjAp`vbdU_^C*Xm|_FGBt1cK}&aX(Ij?p!PO2cxa|?IiB`k1t*FvlxEXE61pfoY_21 zZ05w33&%Xhg#QFq_PgUP`j1C@JRavt9>-hCf&=b%MZ|+7ca=%|;mgA+|4+sIb7rTapt&S3KiJ!aE|( zctRuoq|_hmbw#@G1NOtbL9~8%&=Jx6Bx5-8sGa%Qu+L$IHWXmpIocy7{xpNqXMUPD zk35i43o4$(!$Ydd?fl#(wLa8hH?G~i*4S?N)vc}FYmJ7M@D}I2MM(ZNXYG!{$=&;18?p~ZEu{@`j z|EZB?8ru0~rjzyDglB^!I%I7};GA+e=2p5a(sb~;PSQdMnsK5t<1qd$8V+1wEQcKX4^iy!qGJK^K-VW*YdyeaNC ztJNTR*5re|gN=um#e-pQryAe7+7`RlKi#?W2<~B+05)=Mf+Z|k$Yt;r;FWmFhH;{G z3whH_16fjlTvA}_vPpn2 zra?BVAUCxv=J~t^>9j@wvO8j)y(K8j;S08eWS$^dkdZ8+*7yoe$8D8>Vxy8Sh-B*o zayBM3V;ck}#ja*k0@OxZ$yJg9Qb<{lUrT2|Eiyv1%sLr?^v38Ifz0MO0a@sNKuSy)`q7bUTmy^tCyp6GCqGx;>9Em(2wrUs*)%?FA(7 zm`h;Umk5k}6=hQPWddVgLoZ`Da60~VB;=-r+%B1i=gpp{Z;gty=@kd-dQUm zy}OQ%5(gNzGW!j3g@9?7&}wEZoy$$-W^%K+d~Pl`KXN5k%5CMY=HA-a9$8aOSymK9 zHYD+rWbr65l?V|_F+U(UgH@sM-^&-ir2VxoDX)X36H}LTLS>>WG$DwJM8eeYmGH$Z zRIHXIxEC@qMf$^pQ7A!7f_602N&=Ax5~?Fb0}?W_s+tIoB$L>OaHAoY1cjjIBuI&P zko$`$CW}?oJRgNHQ4J)7=tNR=M49L!N?lUoPz_0BM3ZRT#H=_1Xt@ zW7lssc`fq%wz})LkG)13hmg0!ZrGc%^LZr-+=w^MQoDY@ck16>@R`s9q zc4EKd*G}jiH>}i7c)i=?Mn91Ix2qF>UPft;yA%>R2VJH@zqCcN&3P=apLEb)qm?^;$fP+*T(MhhkL& zw;j51Vz)WZU6>wqpESMjr04eHhA`oGqh8v2HQ;X84bG+q%m-d{QV-l`d#-;J_#HoV zoBG3hJ!$nJV(16jLmq^fOseiXUfYX2x9NSzjUhWZWSwI_aJneuL>PFOy0CM)^}_7x z_`CexgaO5g*7oZm*$en?G|-d&!P;dVo4&trquCw!tGO?6->(J@cK;i5qey z^jkdN?bLGsNaE}cf`&je#<$%h5$LI z*L%1$-FBmkr)E%;saM2HrC7eSH5OuVBJ6bsYl67DLB#9XfH$$uH%Aj?W7K)(VI5ELrNNfCsO2wOq@u(=zOOkS@8ztgu z;GN^*ga4%S_G9mh&wu~%54~mo)AAqH)VHNSz>og^&p#QB=fCxS^Z2hVL%O!@b&kWg zH#gsFRgP+>wMUJ5boZ|Ju)4G31|L>=_sP@kM_0WM&RR!1{=2u1y$83yb9C>qFn-b7 zoA|^}2ut9_hcXqZ!88MmEYp$KSQ6<3%`pSVNjgQR=?tBvc{)dJI!_nqB3+`F7{%QJ zGm)A!MJ<}98OrDg9i?M*oMu_d0A{f?%a}!!!JASUFIsF=T-M^JOxQSXNGyv~rkCk5 zU7@RN!Ymn3maJk~Lh(sM*%~Me#wF!jsS;JnvU$ZSvT1sytQd*cNHtI@LDh1?+_Z{x zvn&!Vl`&CfTgkF+E6qdLyl*b?qvGS^XUon1zL*}w~< z+URGX5w>E0I>?8uqC|lLTcbr*#A}HH%ReS;U6B(ByeIXZWws&kz;KdGMXEE+DjH)< zM?S%lNCk2Rj)?#3v1b%_SD1;^p!8MjSqihTaF?(wa&}yS0?t8H09RhD0B&G01^Nt8 z61cNTtAN-`uR-()N=xM=;#ol4HxYOFHBu4UF=7~HtP(|li*&7QS_QgMPFb2j{<{SI zEST%oI=0&)!n=eP7tB>_m8v*jjY|;&i7i%?#Kn2@TG=uQzEX@KUe?c{SG7L87|bDg z?{`;={W)yGXPKMVf4MCU7jABS_H*}>LDldZ;1?ggBX%`O{7U~C?i}@%)Gr21X1T|y zx&F%5K)XK&?6JPc=W?I1@AsKO5GwFr&5E@W=sG1-I&Y8vkvR_w@bg7HLpzW9PSx{p zIuo%7l$7DxzyQM!A%t7T-nF)3mjcs#1;WH2M106V3bP#vSxoe9FaN aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..fe06059a19298b989fe5ef630ff59c63111237cd GIT binary patch literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..f2d215e295f47705bf56c11f22c759c52fc43203 GIT binary patch literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv new file mode 100644 index 0000000000000000000000000000000000000000..7b0364a1abd25488c88f8e031c2c94f58b93d5bf GIT binary patch literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..e9736e40d3098473a75e9cd4f6390736cb29f34c GIT binary patch literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..9fc3054e32464ca19f39e147e1cd114bdb85369c GIT binary patch literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv new file mode 100644 index 0000000000000000000000000000000000000000..a5eebc7a853d9d71538903b6e37234f5bf3caccd GIT binary patch literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/079_Signature.mv b/vm/stdlib/compiled/12/stdlib/079_Signature.mv new file mode 100644 index 0000000000000000000000000000000000000000..e37f2baf06e884792c42fdb4bf03bae888c3fe3f GIT binary patch literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv new file mode 100644 index 0000000000000000000000000000000000000000..aa92ddbcab19b8289c18c5d75dd714715d8e0637 GIT binary patch literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv new file mode 100644 index 0000000000000000000000000000000000000000..5fda8be8e329a6340402ae254ff9ad5ca5502fe0 GIT binary patch literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv new file mode 100644 index 0000000000000000000000000000000000000000..1f79c3cc854a84bffd1177410c4789f649e19618 GIT binary patch literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/084_String.mv b/vm/stdlib/compiled/12/stdlib/084_String.mv new file mode 100644 index 0000000000000000000000000000000000000000..a718cd408f0896e17de2b6da730b431dfe2a7a7c GIT binary patch literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/085_Table.mv b/vm/stdlib/compiled/12/stdlib/085_Table.mv new file mode 100644 index 0000000000000000000000000000000000000000..297bb011c407ad2b2ebe612110e13206bca96db8 GIT binary patch literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv new file mode 100644 index 0000000000000000000000000000000000000000..6f1c313bfae799fc73233ce143be0b2bccc22e96 GIT binary patch literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..dca83ebaca3cf7db7eab5177d516a9c39ff491cb GIT binary patch literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..23b7501a57b223bcba74253c5b6edff659f4d604 GIT binary patch literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/091_U256.mv b/vm/stdlib/compiled/12/stdlib/091_U256.mv new file mode 100644 index 0000000000000000000000000000000000000000..e923809b5f9f07f184e1012756e5235bafd57552 GIT binary patch literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv new file mode 100644 index 0000000000000000000000000000000000000000..e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249 GIT binary patch literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/052_Epoch.mv b/vm/stdlib/compiled/latest/stdlib/052_Epoch.mv new file mode 100644 index 0000000000000000000000000000000000000000..2aa602ac288885c085b6f24d688ec4ea3d5c4b81 GIT binary patch literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv new file mode 100644 index 0000000000000000000000000000000000000000..2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05 GIT binary patch literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv new file mode 100644 index 0000000000000000000000000000000000000000..7d3359f06d3ca7172b76018eb71bb97aadf699d0 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv new file mode 100644 index 0000000000000000000000000000000000000000..b8584e7754d63d0f975320ee01be7b17fc125d7a GIT binary patch literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/060_Offer.mv b/vm/stdlib/compiled/latest/stdlib/060_Offer.mv new file mode 100644 index 0000000000000000000000000000000000000000..297fc8eb9bf27b993901390d58ac6b21e2ea943c GIT binary patch literal 538 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 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv new file mode 100644 index 0000000000000000000000000000000000000000..9b6c88aad4ff46335891d5a6b5f3c1745f20396d GIT binary patch literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv new file mode 100644 index 0000000000000000000000000000000000000000..44d5f27272aecd2f94a15d3e82260ffe92ab259a GIT binary patch literal 1493 zcmY*ZOLE&r5bf?6V1OAAACaO&N{*F}EuTP!E$35CB~@M|StMDrAQ6;NrT`WQ+0iAm zNRMiyD+2-#$lZ7MwgNIG0h_jLDr{icbTcgKGj7XStbn)I3Y;unfMOe=1G|C?NJ@gD)_8(J5`4$C#weze5OgJjt|aoc4JM`q>;cVqaJ`P>2;#kH7>%NFgAa?q0EUP0NyqM=@Q!I+S{`Tu1+^_1os@KB0->sUWD&6mkt1_?s=M`t(*m!n% znXj6lGcyL)xB8;Z&dNm-?%dh+O%wmPnrBV^Wwy#Lie=GkORJk^FwfUbUER*|>s50* z{NJwQRow4Ixu}Axys_l7f;ZteMcM2KovS)48?RkwP30HsStm`gSG_6M(k`aXo140v zU1XQ9!sqPm2;4ezjtjc^)R?Td6bFnPeFY~!&U02t+%71F= z?6R?YTa{$XI?v{}yUJj5Ae(l(v3T|%_7Ku?$VIVSZp&HpcOgM<_NDfHiH3^8A6z=4J&O5sFm11Ua%&%p@{-$XU^a%>|+6U;dKp&y#aAm`(1k7?~zWF#GcZWY6|7{WgFbI~}FZfp*@e2;+Z}Fm?!<5V@O8{rY&kmVoO5hPi#c_WC{PAeal;XWRdiD$=;BkuVV!llLzdy~Pz(2R!T;ba1+5hWUdMxtgSu^HnT zLypK3%QNyii{!}YGR-vH;#1%zp(?F3Qc?V)RuO@+BTC6df{;$lnB-h(u9YDGk??3V z>cpxJA}N(ZinIw5Brf?rlh|9I#d>6S6}V4hib8hHWp7;NHh%N=>vz4^b?aU&7OmTC z+@^Q2a8>Y2?^?=DFJ1fH+R^IFZ$j(e--Np5!K;%`?U|d;cCBoUM)lJsZbGRB+x6j4`jab!53@)gG>D;FR9!uM0{k?xYg|W{Z%>o0YyEO(7B^+N%Sq09VaiTS-qv-np$^45-`q6KdMACgaNqOm zqUry?&%vAXx93+ESNW^6vv1F@u54`Yu4f%3E{aeb6m)QH$nQiI`?H)>?qgmOCjaQe zhuoLN%IVgv=pbF2xv~knF5T+{br|YElAoVVd8TD>Y$E_>tt;?_L zvMKhbX7{4=h9oX&BtHFN#wO#dP_);zukK$=#tvuiKDd+3ZRX@w#R3&hVi7kZBwdL5%e0IAyc~`oLih1ft=bWi*Tm3|BZZR`zk4u`${tqwB&jlNdQi zD-0Mh<}W$pv7D%gfH6VD{K0J0w-k91M+!1NfRWB4(06zeUCm!HG@YpwykSahRAzpYyV$RwwX(GDPj=!C6X3m^5<1=3^{9$VdArqrj zvbsV)e2$eb$(;Hf`3CF1(-*q&gY9TP*`KiQ+1CUNF(QZ}1*u3wIx>)nEMy~w3aE&t zkb_)QG7KFR3Od583UbdgjIi^Ll$3%ua^IC0osf81iWwp6$;{{6mtrQd5IRJ zI0b?%f{D5Wf-Qq6o(5q`q%Er$8D}&dDQ69gb$bP2qU+i@X}ShE>dQ8mN`&9AAjX@x zfULKQQ&@4|b}?4y1q9f+cw!nWg-f$#q^^Snym;OJ%rtf#JE4Y}UJ zDXf%M7C~XXzXY2xD&b;aJjekTc9yb&g%pQU;=4C6tRsaLykcwp7y zZkLCdh$F2QM{Ut5)Z&C!TdjnrDNn5%JmRTH?_O_~$FLzf5l?DCKiCspkqxJA2T^Ab zbohOqq!5RKty*uldE(Tg+I}FSQQ)m0*6K+Rb~$Y{YYg6djjfTwY>FOFv!K^koACjU zjAp`vbdU_^C*Xm|_FGBt1cK}&aX(Ij?p!PO2cxa|?IiB`k1t*FvlxEXE61pfoY_21 zZ05w33&%Xhg#QFq_PgUP`j1C@JRavt9>-hCf&=b%MZ|+7ca=%|;mgA+|4+sIb7rTapt&S3KiJ!aE|( zctRuoq|_hmbw#@G1NOtbL9~8%&=Jx6Bx5-8sGa%Qu+L$IHWXmpIocy7{xpNqXMUPD zk35i43o4$(!$Ydd?fl#(wLa8hH?G~i*4S?N)vc}FYmJ7M@D}I2MM(ZNXYG!{$=&;18?p~ZEu{@`j z|EZB?8ru0~rjzyDglB^!I%I7};GA+e=2p5a(sb~;PSQdMnsK5t<1qd$8V+1wEQcKX4^iy!qGJK^K-VW*YdyeaNC ztJNTR*5re|gN=um#e-pQryAe7+7`RlKi#?W2<~B+05)=Mf+Z|k$Yt;r;FWmFhH;{G z3whH_16fjlTvA}_vPpn2 zra?BVAUCxv=J~t^>9j@wvO8j)y(K8j;S08eWS$^dkdZ8+*7yoe$8D8>Vxy8Sh-B*o zayBM3V;ck}#ja*k0@OxZ$yJg9Qb<{lUrT2|Eiyv1%sLr?^v38Ifz0MO0a@sNKuSy)`q7bUTmy^tCyp6GCqGx;>9Em(2wrUs*)%?FA(7 zm`h;Umk5k}6=hQPWddVgLoZ`Da60~VB;=-r+%B1i=gpp{Z;gty=@kd-dQUm zy}OQ%5(gNzGW!j3g@9?7&}wEZoy$$-W^%K+d~Pl`KXN5k%5CMY=HA-a9$8aOSymK9 zHYD+rWbr65l?V|_F+U(UgH@sM-^&-ir2VxoDX)X36H}LTLS>>WG$DwJM8eeYmGH$Z zRIHXIxEC@qMf$^pQ7A!7f_602N&=Ax5~?Fb0}?W_s+tIoB$L>OaHAoY1cjjIBuI&P zko$`$CW}?oJRgNHQ4J)7=tNR=M49L!N?lUoPz_0BM3ZRT#H=_1Xt@ zW7lssc`fq%wz})LkG)13hmg0!ZrGc%^LZr-+=w^MQoDY@ck16>@R`s9q zc4EKd*G}jiH>}i7c)i=?Mn91Ix2qF>UPft;yA%>R2VJH@zqCcN&3P=apLEb)qm?^;$fP+*T(MhhkL& zw;j51Vz)WZU6>wqpESMjr04eHhA`oGqh8v2HQ;X84bG+q%m-d{QV-l`d#-;J_#HoV zoBG3hJ!$nJV(16jLmq^fOseiXUfYX2x9NSzjUhWZWSwI_aJneuL>PFOy0CM)^}_7x z_`CexgaO5g*7oZm*$en?G|-d&!P;dVo4&trquCw!tGO?6->(J@cK;i5qey z^jkdN?bLGsNaE}cf`&je#<$%h5$LI z*L%1$-FBmkr)E%;saM2HrC7eSH5OuVBJ6bsYl67DLB#9XfH$$uH%Aj?W7K)(VI5ELrNNfCsO2wOq@u(=zOOkS@8ztgu z;GN^*ga4%S_G9mh&wu~%54~mo)AAqH)VHNSz>og^&p#QB=fCxS^Z2hVL%O!@b&kWg zH#gsFRgP+>wMUJ5boZ|Ju)4G31|L>=_sP@kM_0WM&RR!1{=2u1y$83yb9C>qFn-b7 zoA|^}2ut9_hcXqZ!88MmEYp$KSQ6<3%`pSVNjgQR=?tBvc{)dJI!_nqB3+`F7{%QJ zGm)A!MJ<}98OrDg9i?M*oMu_d0A{f?%a}!!!JASUFIsF=T-M^JOxQSXNGyv~rkCk5 zU7@RN!Ymn3maJk~Lh(sM*%~Me#wF!jsS;JnvU$ZSvT1sytQd*cNHtI@LDh1?+_Z{x zvn&!Vl`&CfTgkF+E6qdLyl*b?qvGS^XUon1zL*}w~< z+URGX5w>E0I>?8uqC|lLTcbr*#A}HH%ReS;U6B(ByeIXZWws&kz;KdGMXEE+DjH)< zM?S%lNCk2Rj)?#3v1b%_SD1;^p!8MjSqihTaF?(wa&}yS0?t8H09RhD0B&G01^Nt8 z61cNTtAN-`uR-()N=xM=;#ol4HxYOFHBu4UF=7~HtP(|li*&7QS_QgMPFb2j{<{SI zEST%oI=0&)!n=eP7tB>_m8v*jjY|;&i7i%?#Kn2@TG=uQzEX@KUe?c{SG7L87|bDg z?{`;={W)yGXPKMVf4MCU7jABS_H*}>LDldZ;1?ggBX%`O{7U~C?i}@%)Gr21X1T|y zx&F%5K)XK&?6JPc=W?I1@AsKO5GwFr&5E@W=sG1-I&Y8vkvR_w@bg7HLpzW9PSx{p zIuo%7l$7DxzyQM!A%t7T-nF)3mjcs#1;WH2M106V3bP#vSxoe9FaN aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..fe06059a19298b989fe5ef630ff59c63111237cd GIT binary patch literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..f2d215e295f47705bf56c11f22c759c52fc43203 GIT binary patch literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv new file mode 100644 index 0000000000000000000000000000000000000000..7b0364a1abd25488c88f8e031c2c94f58b93d5bf GIT binary patch literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..e9736e40d3098473a75e9cd4f6390736cb29f34c GIT binary patch literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..9fc3054e32464ca19f39e147e1cd114bdb85369c GIT binary patch literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv new file mode 100644 index 0000000000000000000000000000000000000000..a5eebc7a853d9d71538903b6e37234f5bf3caccd GIT binary patch literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/079_Signature.mv b/vm/stdlib/compiled/latest/stdlib/079_Signature.mv new file mode 100644 index 0000000000000000000000000000000000000000..e37f2baf06e884792c42fdb4bf03bae888c3fe3f GIT binary patch literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv new file mode 100644 index 0000000000000000000000000000000000000000..aa92ddbcab19b8289c18c5d75dd714715d8e0637 GIT binary patch literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv new file mode 100644 index 0000000000000000000000000000000000000000..5fda8be8e329a6340402ae254ff9ad5ca5502fe0 GIT binary patch literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv new file mode 100644 index 0000000000000000000000000000000000000000..1f79c3cc854a84bffd1177410c4789f649e19618 GIT binary patch literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/084_String.mv b/vm/stdlib/compiled/latest/stdlib/084_String.mv new file mode 100644 index 0000000000000000000000000000000000000000..a718cd408f0896e17de2b6da730b431dfe2a7a7c GIT binary patch literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/085_Table.mv b/vm/stdlib/compiled/latest/stdlib/085_Table.mv new file mode 100644 index 0000000000000000000000000000000000000000..297bb011c407ad2b2ebe612110e13206bca96db8 GIT binary patch literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv new file mode 100644 index 0000000000000000000000000000000000000000..6f1c313bfae799fc73233ce143be0b2bccc22e96 GIT binary patch literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..dca83ebaca3cf7db7eab5177d516a9c39ff491cb GIT binary patch literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv new file mode 100644 index 0000000000000000000000000000000000000000..23b7501a57b223bcba74253c5b6edff659f4d604 GIT binary patch literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/091_U256.mv b/vm/stdlib/compiled/latest/stdlib/091_U256.mv new file mode 100644 index 0000000000000000000000000000000000000000..e923809b5f9f07f184e1012756e5235bafd57552 GIT binary patch literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& literal 0 HcmV?d00001 diff --git a/vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv new file mode 100644 index 0000000000000000000000000000000000000000..e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249 GIT binary patch literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 literal 0 HcmV?d00001 diff --git a/vm/types/src/dag_block_metadata.rs b/vm/types/src/dag_block_metadata.rs new file mode 100644 index 0000000000..db785968c0 --- /dev/null +++ b/vm/types/src/dag_block_metadata.rs @@ -0,0 +1,146 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +// Copyright (c) The Diem Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::account_address::AccountAddress; +use crate::account_config::genesis_address; +use crate::genesis_config::ChainId; +use crate::transaction::authenticator::AuthenticationKey; +use bcs_ext::Sample; +use serde::{Deserialize, Deserializer, Serialize}; +use starcoin_crypto::hash::PlainCryptoHash; +use starcoin_crypto::{ + hash::{CryptoHash, CryptoHasher}, + HashValue, +}; + +/// Struct that will be persisted on chain to store the information of the current block. +/// +/// The flow will look like following: +/// 1. The executor will pass this struct to VM at the begin of a block proposal. +/// 2. The VM will use this struct to create a special system transaction that will modify the on +/// chain resource that represents the information of the current block. This transaction can't +/// be emitted by regular users and is generated by each of the miners on the fly. Such +/// transaction will be executed before all of the user-submitted transactions in the blocks. +/// 3. Once that special resource is modified, the other user transactions can read the consensus +/// info by calling into the read method of that resource, which would thus give users the +/// information such as the current block number. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, CryptoHasher, CryptoHash)] +//TODO rename to DagBlockMetadataTransaction +pub struct DagBlockMetadata { + #[serde(skip)] + id: Option, + /// Parent block hash. + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + author_auth_key: Option, + chain_id: ChainId, + parent_gas_used: u64, +} + +impl DagBlockMetadata { + pub fn new( + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + author_auth_key: Option, + chain_id: ChainId, + parent_gas_used: u64, + ) -> Self { + let mut txn = Self { + id: None, + parent_hash, + timestamp, + author, + author_auth_key, + chain_id, + parent_gas_used, + }; + txn.id = Some(txn.crypto_hash()); + txn + } + + pub fn into_inner( + self, + ) -> ( + Vec, + u64, + AccountAddress, + Option, + ChainId, + u64, + ) { + ( + self.parent_hash, + self.timestamp, + self.author, + self.author_auth_key, + self.chain_id, + self.parent_gas_used, + ) + } + + pub fn parent_hash(&self) -> Vec { + self.parent_hash.clone() + } + + pub fn timestamp(&self) -> u64 { + self.timestamp + } + + pub fn chain_id(&self) -> ChainId { + self.chain_id + } + + pub fn id(&self) -> HashValue { + self.id + .expect("DagBlockMetadata's id should been Some after init.") + } + + pub fn author(&self) -> AccountAddress { + self.author + } +} + +impl<'de> Deserialize<'de> for DagBlockMetadata { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(rename = "DagBlockMetadata")] + struct DagBlockMetadataData { + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + author_auth_key: Option, + chain_id: ChainId, + parent_gas_used: u64, + } + let data = DagBlockMetadataData::deserialize(deserializer)?; + Ok(Self::new( + data.parent_hash, + data.timestamp, + data.author, + data.author_auth_key, + data.chain_id, + data.parent_gas_used, + )) + } +} + +impl Sample for DagBlockMetadata { + fn sample() -> Self { + Self::new( + vec![HashValue::zero()], + 0, + genesis_address(), + None, + ChainId::test(), + 0, + ) + } +} From 6e46549a73b0fc18e10777ff6e70d1d512f1d154 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Mon, 16 Oct 2023 18:42:10 +0800 Subject: [PATCH 08/17] merge chain refactor --- benchmarks/src/chain.rs | 2 +- block-relayer/src/block_relayer.rs | 8 +- chain/api/src/chain.rs | 15 +- chain/chain-notify/src/lib.rs | 3 +- chain/mock/src/mock_chain.rs | 4 +- chain/service/src/chain_service.rs | 7 +- chain/src/chain.rs | 77 +- cmd/db-exporter/src/main.rs | 22 +- cmd/replay/src/main.rs | 8 +- cmd/starcoin/src/dev/gen_block_cmd.rs | 4 +- config/src/genesis_config.rs | 2 +- genesis/generated/halley/genesis | Bin 116028 -> 119025 bytes miner/src/create_block_template/mod.rs | 6 +- network/api/src/messages.rs | 8 +- network/src/service.rs | 4 +- node/src/node.rs | 10 +- sync/src/block_connector/write_block_chain.rs | 28 + sync/src/sync.rs | 4 +- sync/src/tasks/block_sync_task.rs | 3 +- test-helper/src/chain.rs | 2 +- types/src/block.rs | 25 +- types/src/dag_block.rs | 947 ++++++++++++++++++ types/src/header.rs | 20 +- types/src/lib.rs | 1 + types/src/system_events.rs | 3 +- 25 files changed, 1073 insertions(+), 140 deletions(-) create mode 100644 types/src/dag_block.rs diff --git a/benchmarks/src/chain.rs b/benchmarks/src/chain.rs index e46462471c..a9af4ec157 100644 --- a/benchmarks/src/chain.rs +++ b/benchmarks/src/chain.rs @@ -71,7 +71,7 @@ impl ChainBencher { let block = ConsensusStrategy::Dummy .create_block(block_template, self.net.time_service().as_ref()) .unwrap(); - self.chain.write().apply(block, None, &mut None).unwrap(); + self.chain.write().apply(block, None).unwrap(); } } diff --git a/block-relayer/src/block_relayer.rs b/block-relayer/src/block_relayer.rs index 88767d572c..4a180353b6 100644 --- a/block-relayer/src/block_relayer.rs +++ b/block-relayer/src/block_relayer.rs @@ -78,7 +78,7 @@ impl BlockRelayer { &self, network: NetworkServiceRef, executed_block: Arc, - tips_header: Option>, + tips_hash: Option> ) { if !self.is_nearly_synced() { debug!("[block-relay] Ignore NewHeadBlock event because the node has not been synchronized yet."); @@ -88,7 +88,7 @@ impl BlockRelayer { let compact_block_msg = CompactBlockMessage::new( compact_block, executed_block.block_info.clone(), - tips_header, + tips_hash ); network.broadcast(NotificationMessage::CompactBlock(Box::new( compact_block_msg, @@ -247,7 +247,7 @@ impl BlockRelayer { block_connector_service.notify(PeerNewBlock::new( peer_id, block, - compact_block_msg.message.tips_header, + compact_block_msg.message.tips_hash, ))?; } Ok(()) @@ -313,7 +313,7 @@ impl EventHandler for BlockRelayer { return; } }; - self.broadcast_compact_block(network, event.0, event.1); + self.broadcast_compact_block(network, event.0,event.1); } } diff --git a/chain/api/src/chain.rs b/chain/api/src/chain.rs index 4249859727..153de45a90 100644 --- a/chain/api/src/chain.rs +++ b/chain/api/src/chain.rs @@ -84,7 +84,7 @@ pub trait ChainReader { fn execute( &self, block: VerifiedBlock, - dag_block_parents: Option, + parents_hash: Option>, ) -> Result; /// Get chain transaction infos fn get_transaction_infos( @@ -113,19 +113,10 @@ pub trait ChainReader { pub trait ChainWriter { fn can_connect(&self, executed_block: &ExecutedBlock) -> bool; /// Connect a executed block to current chain. - fn connect( - &mut self, - executed_block: ExecutedBlock, - next_tips: &mut Option>, - ) -> Result; + fn connect(&mut self, executed_block: ExecutedBlock) -> Result; /// Verify, Execute and Connect block to current chain. - fn apply( - &mut self, - block: Block, - dag_block_next_parent: Option, - next_tips: &mut Option>, - ) -> Result; + fn apply(&mut self, block: Block,parents_hash:Option>) -> Result; fn chain_state(&mut self) -> &ChainStateDB; diff --git a/chain/chain-notify/src/lib.rs b/chain/chain-notify/src/lib.rs index 021a19f65a..c606e6ddf9 100644 --- a/chain/chain-notify/src/lib.rs +++ b/chain/chain-notify/src/lib.rs @@ -52,11 +52,10 @@ impl EventHandler for ChainNotifyHandlerService { item: NewHeadBlock, ctx: &mut ServiceContext, ) { - let NewHeadBlock(block_detail, _dag_parents, _next_tips) = item; + let NewHeadBlock(block_detail,tips_hash) = item; let block = block_detail.block(); // notify header. self.notify_new_block(block, ctx); - // notify events if let Err(e) = self.notify_events(block, self.store.clone(), ctx) { error!(target: "pubsub", "fail to notify events to client, err: {}", &e); diff --git a/chain/mock/src/mock_chain.rs b/chain/mock/src/mock_chain.rs index 0cc234cdf4..46c6135c78 100644 --- a/chain/mock/src/mock_chain.rs +++ b/chain/mock/src/mock_chain.rs @@ -120,8 +120,8 @@ impl MockChain { .create_block(template, self.net.time_service().as_ref()) } - pub fn apply(&mut self, block: Block, dag_parent: Option) -> Result<()> { - self.head.apply(block, dag_parent, &mut None)?; + pub fn apply(&mut self, block: Block, parents_hash: Option>) -> Result<()> { + self.head.apply(block, parents_hash)?; Ok(()) } diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index e93d41e2d5..36f48984cb 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -92,11 +92,9 @@ impl EventHandler for ChainReaderService { fn handle_event(&mut self, event: NewHeadBlock, _ctx: &mut ServiceContext) { let new_head = event.0.block().header(); if let Err(e) = if self.inner.get_main().can_connect(event.0.as_ref()) { - let mut next_tips = event.2.clone(); - match self .inner - .update_chain_head(event.0.as_ref().clone(), &mut next_tips) + .update_chain_head(event.0.as_ref().clone()) { Ok(_) => self.inner.update_dag_accumulator(new_head.id()), Err(e) => Err(e), @@ -323,9 +321,8 @@ impl ChainReaderServiceInner { pub fn update_chain_head( &mut self, block: ExecutedBlock, - next_tips: &mut Option>, ) -> Result<()> { - self.main.connect(block, next_tips)?; + self.main.connect(block)?; Ok(()) } diff --git a/chain/src/chain.rs b/chain/src/chain.rs index ddc2cb4b6b..814d81f799 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -13,7 +13,8 @@ use starcoin_chain_api::{ verify_block, ChainReader, ChainWriter, ConnectBlockError, EventWithProof, ExcludedTxns, ExecutedBlock, MintedUncleNumber, TransactionInfoWithProof, VerifiedBlock, VerifyBlockField, }; -use starcoin_consensus::Consensus; +use starcoin_consensus::dag::types::ghostdata::GhostdagData; +use starcoin_consensus::{BlockDAG, Consensus, FlexiDagStorage}; use starcoin_crypto::hash::PlainCryptoHash; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; @@ -28,6 +29,7 @@ use starcoin_time_service::TimeService; use starcoin_types::block::BlockIdAndNumber; use starcoin_types::contract_event::ContractEventInfo; use starcoin_types::filter::Filter; +use starcoin_types::header::DagHeader; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; use starcoin_types::transaction::RichTransactionInfo; use starcoin_types::{ @@ -159,7 +161,6 @@ impl BlockChain { storage.get_accumulator_store(AccumulatorStoreType::Block), ); let statedb = ChainStateDB::new(storage.clone().into_super_arc(), None); - let genesis_id = genesis_block.header.id(); let executed_block = Self::execute_block_and_save( storage.as_ref(), @@ -185,7 +186,6 @@ impl BlockChain { new_tips, dag_accumulator.get_info(), )?; - Self::new(time_service, executed_block.block.id(), storage, None) } @@ -370,27 +370,22 @@ impl BlockChain { V::verify_block(self, block) } - pub fn apply_with_verifier( - &mut self, - block: Block, - dag_block_parent: Option, - next_tips: &mut Option>, - ) -> Result + pub fn apply_with_verifier(&mut self, block: Block, parents_hash: Option>) -> Result where V: BlockVerifier, { let verified_block = self.verify_with_verifier::(block)?; watch(CHAIN_WATCH_NAME, "n1"); - let executed_block = self.execute(verified_block, dag_block_parent)?; + let executed_block = self.execute(verified_block, parents_hash)?; watch(CHAIN_WATCH_NAME, "n2"); - self.connect(executed_block, next_tips) + self.connect(executed_block) } //TODO remove this function. pub fn update_chain_head( &mut self, block: Block, - dag_parent: Option, + parents_hash: Option>, ) -> Result { let block_info = self .storage @@ -400,9 +395,8 @@ impl BlockChain { ExecutedBlock { block, block_info, - dag_parent, - }, - &mut Some(vec![]), + parents_hash, + } ) } @@ -415,8 +409,9 @@ impl BlockChain { epoch: &Epoch, parent_status: Option, block: Block, - dag_block_parent: Option, vm_metrics: Option, + parents_hash: Option> + ) -> Result { let header = block.header(); debug_assert!(header.is_genesis() || parent_status.is_some()); @@ -427,8 +422,7 @@ impl BlockChain { let mut t = match &parent_status { None => vec![], Some(parent) => { - let block_metadata = - block.to_metadata(parent.head().gas_used(), dag_block_parent); + let block_metadata = block.to_metadata(parent.head().gas_used()); vec![Transaction::BlockMetadata(block_metadata)] } }; @@ -575,7 +569,7 @@ impl BlockChain { Ok(ExecutedBlock { block, block_info, - dag_parent: dag_block_parent, + parents_hash, }) } @@ -688,7 +682,7 @@ impl ChainReader for BlockChain { ExecutedBlock::new( self.status.head.clone(), self.status.status.info.clone(), - self.status.status.get_last_tip_block_id(), + self.status.status.tips_hash.clone(), ) } @@ -885,6 +879,7 @@ impl ChainReader for BlockChain { uncles, self.storage.clone(), self.vm_metrics.clone(), + //TODO: check missing blocks need to be clean ) } @@ -919,11 +914,7 @@ impl ChainReader for BlockChain { FullVerifier::verify_block(self, block) } - fn execute( - &self, - verified_block: VerifiedBlock, - dag_block_parent: Option, - ) -> Result { + fn execute(&self, verified_block: VerifiedBlock, parents_hash: Option>) -> Result { Self::execute_block_and_save( self.storage.as_ref(), self.statedb.fork(), @@ -932,8 +923,8 @@ impl ChainReader for BlockChain { &self.epoch, Some(self.status.status.clone()), verified_block.0, - dag_block_parent, self.vm_metrics.clone(), + parents_hash ) } @@ -1156,12 +1147,7 @@ impl ChainWriter for BlockChain { == executed_block.block().header().parent_hash(); } } - - fn connect( - &mut self, - executed_block: ExecutedBlock, - next_tips: &mut Option>, - ) -> Result { + fn connect(&mut self, executed_block: ExecutedBlock) -> Result { let (block, block_info) = (executed_block.block(), executed_block.block_info()); if self.status.status.tips_hash.is_some() { let mut tips = self.status.status.tips_hash.clone().unwrap(); @@ -1189,20 +1175,18 @@ impl ChainWriter for BlockChain { ); self.statedb = ChainStateDB::new(self.storage.clone().into_super_arc(), Some(state_root)); - match next_tips { - Some(tips) => { + let tips = self.status.status.tips_hash.clone(); + let next_tips = match tips { + Some(mut tips) => { if !tips.contains(&block.header().id()) { - tips.push(block.header().id()) + tips.push(block.header().id()); } + Some(tips) } - None => (), - } + None => None, + }; self.status = ChainStatusWithBlock { - status: ChainStatus::new( - block.header().clone(), - block_info.clone(), - next_tips.clone(), - ), + status: ChainStatus::new(block.header().clone(), block_info.clone(), next_tips), head: block.clone(), }; if self.epoch.end_block_number() == block.header().number() { @@ -1217,13 +1201,8 @@ impl ChainWriter for BlockChain { Ok(executed_block) } - fn apply( - &mut self, - block: Block, - dag_block_next_parent: Option, - next_tips: &mut Option>, - ) -> Result { - self.apply_with_verifier::(block, dag_block_next_parent, next_tips) + fn apply(&mut self, block: Block, parents_hash: Option>) -> Result { + self.apply_with_verifier::(block, parents_hash) } fn chain_state(&mut self) -> &ChainStateDB { diff --git a/cmd/db-exporter/src/main.rs b/cmd/db-exporter/src/main.rs index 7454f8557d..220a7e6879 100644 --- a/cmd/db-exporter/src/main.rs +++ b/cmd/db-exporter/src/main.rs @@ -758,13 +758,13 @@ pub fn apply_block( let block_number = block.header().number(); match verifier { Verifier::Basic => { - chain.apply_with_verifier::(block, None, &mut None)? + chain.apply_with_verifier::(block, None)? } Verifier::Consensus => { - chain.apply_with_verifier::(block, None, &mut None)? + chain.apply_with_verifier::(block, None)? } - Verifier::Full => chain.apply_with_verifier::(block, None, &mut None)?, - Verifier::None => chain.apply_with_verifier::(block, None, &mut None)?, + Verifier::Full => chain.apply_with_verifier::(block, None)?, + Verifier::None => chain.apply_with_verifier::(block, None)?, }; // apply block then flush startup_info for breakpoint resume let startup_info = StartupInfo::new(block_hash); @@ -944,7 +944,7 @@ pub fn execute_transaction_with_create_account( println!("trans {}", block.transactions().len()); } let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None, )?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -967,7 +967,7 @@ pub fn execute_transaction_with_miner_create_account( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None,)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -996,7 +996,7 @@ pub fn execute_transaction_with_miner_create_account( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None, )?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1019,7 +1019,7 @@ pub fn execute_empty_transaction_with_miner( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None, )?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -1046,7 +1046,7 @@ pub fn execute_empty_transaction_with_miner( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None, )?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1070,7 +1070,7 @@ pub fn execute_transaction_with_fixed_account( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -1098,7 +1098,7 @@ pub fn execute_transaction_with_fixed_account( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, &mut None)?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; diff --git a/cmd/replay/src/main.rs b/cmd/replay/src/main.rs index 000c038e29..9913095d62 100644 --- a/cmd/replay/src/main.rs +++ b/cmd/replay/src/main.rs @@ -133,22 +133,22 @@ fn main() -> anyhow::Result<()> { match opts.verifier { Verifier::Basic => { chain2 - .apply_with_verifier::(block, None, &mut None) + .apply_with_verifier::(block, None) .unwrap(); } Verifier::Consensus => { chain2 - .apply_with_verifier::(block, None, &mut None) + .apply_with_verifier::(block, None) .unwrap(); } Verifier::None => { chain2 - .apply_with_verifier::(block, None, &mut None) + .apply_with_verifier::(block, None) .unwrap(); } Verifier::Full => { chain2 - .apply_with_verifier::(block, None, &mut None) + .apply_with_verifier::(block, None) .unwrap(); } }; diff --git a/cmd/starcoin/src/dev/gen_block_cmd.rs b/cmd/starcoin/src/dev/gen_block_cmd.rs index 958d399112..e3156d8586 100644 --- a/cmd/starcoin/src/dev/gen_block_cmd.rs +++ b/cmd/starcoin/src/dev/gen_block_cmd.rs @@ -1,12 +1,10 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use std::time::Duration; - use crate::cli_state::CliState; use crate::view::{ExecuteResultView, TransactionOptions}; use crate::StarcoinOpt; -use anyhow::{anyhow, ensure, Result}; +use anyhow::{ensure, Result}; use clap::Parser; use scmd::{CommandAction, ExecContext}; use starcoin_transaction_builder::build_empty_script; diff --git a/config/src/genesis_config.rs b/config/src/genesis_config.rs index afb767f055..bc60c1fe3c 100644 --- a/config/src/genesis_config.rs +++ b/config/src/genesis_config.rs @@ -833,7 +833,7 @@ pub static G_HALLEY_CONFIG: Lazy = Lazy::new(|| { GenesisConfig { genesis_block_parameter: GenesisBlockParameterConfig::Static(GenesisBlockParameter { - parent_hash: HashValue::sha3_256_of(b"starcoin_halley"), + parent_hash: HashValue::new(starcoin_types::blockhash::ORIGIN), timestamp: 1693798675000, difficulty: 100.into(), }), diff --git a/genesis/generated/halley/genesis b/genesis/generated/halley/genesis index 0b31f956caae41e0547e5a3495ed8099a9ff3be7..f78101314e9b41ce7fd295d146624a58951da6b8 100644 GIT binary patch delta 24362 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 diff --git a/miner/src/create_block_template/mod.rs b/miner/src/create_block_template/mod.rs index c8f40318a2..09f260b414 100644 --- a/miner/src/create_block_template/mod.rs +++ b/miner/src/create_block_template/mod.rs @@ -111,10 +111,9 @@ impl ActorService for BlockBuilderService { impl EventHandler for BlockBuilderService { fn handle_event(&mut self, msg: NewHeadBlock, _ctx: &mut ServiceContext) { - let mut next_tips = msg.2.clone(); if let Err(e) = self .inner - .update_chain(msg.0.as_ref().clone(), &mut next_tips) + .update_chain(msg.0.as_ref().clone()) { error!("err : {:?}", e) } @@ -247,12 +246,11 @@ where pub fn update_chain( &mut self, block: ExecutedBlock, - next_tips: &mut Option>, ) -> Result<()> { let current_header = self.chain.current_header(); let current_id = current_header.id(); if self.chain.can_connect(&block) { - self.chain.connect(block, next_tips)?; + self.chain.connect(block)?; } else { self.chain = BlockChain::new( self.chain.time_service(), diff --git a/network/api/src/messages.rs b/network/api/src/messages.rs index 6f51a79b2d..e6c806aa48 100644 --- a/network/api/src/messages.rs +++ b/network/api/src/messages.rs @@ -48,19 +48,19 @@ impl Sample for TransactionsMessage { pub struct CompactBlockMessage { pub compact_block: CompactBlock, pub block_info: BlockInfo, - pub tips_header: Option>, + pub tips_hash: Option> } impl CompactBlockMessage { pub fn new( compact_block: CompactBlock, block_info: BlockInfo, - tips_header: Option>, + tips_hash: Option> ) -> Self { Self { compact_block, block_info, - tips_header, + tips_hash } } } @@ -70,7 +70,7 @@ impl Sample for CompactBlockMessage { Self::new( CompactBlock::sample(), BlockInfo::sample(), - Some(vec![HashValue::zero()]), + None ) } } diff --git a/network/src/service.rs b/network/src/service.rs index 1b808e7d6c..045538e8a0 100644 --- a/network/src/service.rs +++ b/network/src/service.rs @@ -559,7 +559,7 @@ impl Inner { peer_info.peer_info.update_chain_status(ChainStatus::new( block_header.clone(), compact_block_message.block_info.clone(), - compact_block_message.tips_header.clone(), + compact_block_message.tips_hash.clone(), )); if self.self_peer.known_blocks.contains(&block_id) { @@ -721,7 +721,7 @@ impl Inner { ChainStatus::new( msg.compact_block.header.clone(), msg.block_info.clone(), - msg.tips_header.clone(), + msg.tips_hash.clone(), ) .encode() .expect("Encoding the compact_block.header and block_info must be successful"), diff --git a/node/src/node.rs b/node/src/node.rs index 6eae38c4c6..b432ac84a3 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -371,14 +371,12 @@ impl NodeService { let flex_dag_db = FlexiDagStorage::create_from_path("./smolstc", flex_dag_config) .expect("Failed to create flexidag storage"); - let dag = BlockDAG::new( - DagHeader::new( - genesis.block().header().clone(), - vec![HashValue::new(ORIGIN)], - ), - 3, + let mut dag = BlockDAG::new( + genesis.block().id(), + 8, flex_dag_db, ); + dag.init_with_genesis(DagHeader::new_genesis(genesis.block().header().clone())).expect("dag init with genesis"); registry.put_shared(Arc::new(Mutex::new(dag))).await?; info!( diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 732d278d6e..8fd70aecc1 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -64,6 +64,7 @@ pub enum ConnectOk { MainDuplicate, // the dag block waiting for the time window end DagPending, + DagConnectMissingBlock, } impl ConnectOk { @@ -817,6 +818,33 @@ where return Ok(ConnectOk::ExeConnectMain(executed_block)); } + fn connect_dag_inner( + &mut self, + block: Block, + parents_hash: Vec, + ) -> Result { + let ghost_dag_data = self + .dag + .lock() + .unwrap() + .addToDag(DagHeader::new(block.header, parents_hash))?; + let selected_parent = self + .storage + .get_block_by_hash(ghost_dag_data.selected_parent)? + .expect("selected parent should in storage"); + let mut chain = self.main.fork(selected_parent.header.parent_hash())?; + for blue_hash in ghost_dag_data.mergeset_blues.iter() { + if let Some(blue_block) = self.storage.get_block(blue_hash.to_owned())? { + chain.apply(blue_block); + } else { + error!("Failed to get block {:?}", blue_hash); + return Ok(DagConnectMissingBlock); + } + } + //self.broadcast_new_head(); + Ok(DagConnected) + } + fn connect_inner( &mut self, block: Block, diff --git a/sync/src/sync.rs b/sync/src/sync.rs index c58d22e46d..59e4db783f 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -663,11 +663,11 @@ impl EventHandler for SyncService { impl EventHandler for SyncService { fn handle_event(&mut self, msg: NewHeadBlock, ctx: &mut ServiceContext) { - let NewHeadBlock(block, _dag_parents, next_tips) = msg; + let NewHeadBlock(block, tips_hash) = msg; if self.sync_status.update_chain_status(ChainStatus::new( block.header().clone(), block.block_info.clone(), - next_tips, + tips_hash, )) { self.sync_status.update_dag_accumulator_info( self.storage diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 61c9f52cfb..4200aa99ae 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -1,7 +1,7 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::block_connector::BlockConnectorService; +use crate::block_connector::{BlockConnectedRequest, BlockConnectorService}; use crate::tasks::{BlockConnectedEventHandle, BlockFetcher, BlockLocalStore}; use crate::verified_rpc_client::RpcVerifyError; use anyhow::{format_err, Ok, Result}; @@ -16,6 +16,7 @@ use starcoin_config::G_CRATE_VERSION; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; +use starcoin_service_registry::ServiceRef; use starcoin_storage::BARNARD_HARD_FORK_HASH; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; diff --git a/test-helper/src/chain.rs b/test-helper/src/chain.rs index bc69640391..c184d52519 100644 --- a/test-helper/src/chain.rs +++ b/test-helper/src/chain.rs @@ -27,7 +27,7 @@ pub fn gen_blockchain_with_blocks_for_test(count: u64, net: &ChainNetwork) -> Re let block = block_chain .consensus() .create_block(block_template, net.time_service().as_ref())?; - block_chain.apply(block, None, &mut None)?; + block_chain.apply(block, None,)?; } Ok(block_chain) diff --git a/types/src/block.rs b/types/src/block.rs index c681bb3333..814ca22899 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -718,26 +718,15 @@ impl Block { } } - pub fn to_metadata( - &self, - parent_gas_used: u64, - dag_block_parent: Option, - ) -> BlockMetadata { + pub fn to_metadata(&self, parent_gas_used: u64) -> BlockMetadata { let uncles = self .body .uncles .as_ref() .map(|uncles| uncles.len() as u64) .unwrap_or(0); - - let parent = if dag_block_parent.is_some() { - dag_block_parent.unwrap() - } else { - self.header.parent_hash() - }; - BlockMetadata::new( - parent, + self.header.parent_hash(), self.header.timestamp, self.header.author, self.header.author_auth_key, @@ -913,7 +902,7 @@ impl BlockTemplate { pub fn into_block(self, nonce: u32, extra: BlockHeaderExtra) -> Block { let header = BlockHeader::new( - self.generate_parent_header(), + self.parent_hash, self.timestamp, self.number, self.author, @@ -983,7 +972,7 @@ impl BlockTemplate { pub fn as_raw_block_header(&self) -> RawBlockHeader { RawBlockHeader { - parent_hash: self.generate_parent_header(), + parent_hash: self.parent_hash, timestamp: self.timestamp, number: self.number, author: self.author, @@ -1049,15 +1038,15 @@ impl BlockTemplate { pub struct ExecutedBlock { pub block: Block, pub block_info: BlockInfo, - pub dag_parent: Option, + pub parents_hash: Option>, } impl ExecutedBlock { - pub fn new(block: Block, block_info: BlockInfo, dag_parent: Option) -> Self { + pub fn new(block: Block, block_info: BlockInfo, dag_parents: Option>) -> Self { ExecutedBlock { block, block_info, - dag_parent, + parents_hash: dag_parents, } } diff --git a/types/src/dag_block.rs b/types/src/dag_block.rs new file mode 100644 index 0000000000..bc089a92e5 --- /dev/null +++ b/types/src/dag_block.rs @@ -0,0 +1,947 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::account_address::AccountAddress; +use crate::block::BlockHeaderExtra; +use crate::blockhash::ORIGIN; +use crate::genesis_config::{ChainId, ConsensusStrategy}; +use crate::language_storage::CORE_CODE_ADDRESS; +use crate::transaction::SignedUserTransaction; +use crate::U256; +use bcs_ext::Sample; +use schemars::{self, JsonSchema}; +use serde::{Deserialize, Deserializer, Serialize}; +pub use starcoin_accumulator::accumulator_info::AccumulatorInfo; +use starcoin_crypto::hash::{ACCUMULATOR_PLACEHOLDER_HASH, SPARSE_MERKLE_PLACEHOLDER_HASH}; +use starcoin_crypto::{ + hash::{CryptoHash, CryptoHasher, PlainCryptoHash}, + HashValue, +}; +use starcoin_vm_types::account_config::genesis_address; +use starcoin_vm_types::dag_block_metadata::DagBlockMetadata; +use starcoin_vm_types::transaction::authenticator::AuthenticationKey; +use std::fmt::Formatter; + +/// block timestamp allowed future times +pub const ALLOWED_FUTURE_BLOCKTIME: u64 = 30000; // 30 second; + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, CryptoHasher, CryptoHash, JsonSchema)] +pub struct DagBlockHeader { + #[serde(skip)] + id: Option, + /// Parent hash. + parent_hash: Vec, + /// Block timestamp. + timestamp: u64, + /// Block author. + author: AccountAddress, + /// Block author auth key. + /// this field is deprecated + author_auth_key: Option, + /// The transaction accumulator root hash after executing this block. + txn_accumulator_root: HashValue, + /// The parent block info's block accumulator root hash. + block_accumulator_root: HashValue, + /// The last transaction state_root of this block after execute. + state_root: HashValue, + /// Gas used for contracts execution. + gas_used: u64, + /// Block difficulty + #[schemars(with = "String")] + difficulty: U256, + /// hash for block body + body_hash: HashValue, + /// The chain id + chain_id: ChainId, + /// Consensus nonce field. + nonce: u32, + /// block header extra + extra: BlockHeaderExtra, +} + +impl DagBlockHeader { + pub fn new( + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + txn_accumulator_root: HashValue, + block_accumulator_root: HashValue, + state_root: HashValue, + gas_used: u64, + difficulty: U256, + body_hash: HashValue, + chain_id: ChainId, + nonce: u32, + extra: BlockHeaderExtra, + ) -> DagBlockHeader { + Self::new_with_auth_key( + parent_hash, + timestamp, + author, + None, + txn_accumulator_root, + block_accumulator_root, + state_root, + gas_used, + difficulty, + body_hash, + chain_id, + nonce, + extra, + ) + } + + // the author_auth_key field is deprecated, but keep this fn for compat with old block. + fn new_with_auth_key( + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + author_auth_key: Option, + txn_accumulator_root: HashValue, + block_accumulator_root: HashValue, + state_root: HashValue, + gas_used: u64, + difficulty: U256, + body_hash: HashValue, + chain_id: ChainId, + nonce: u32, + extra: BlockHeaderExtra, + ) -> DagBlockHeader { + let mut header = DagBlockHeader { + id: None, + parent_hash, + block_accumulator_root, + timestamp, + author, + author_auth_key, + txn_accumulator_root, + state_root, + gas_used, + difficulty, + nonce, + body_hash, + chain_id, + extra, + }; + header.id = Some(header.crypto_hash()); + header + } + + pub fn as_pow_header_blob(&self) -> Vec { + let mut blob = Vec::new(); + let raw_header: RawDagBlockHeader = self.to_owned().into(); + let raw_header_hash = raw_header.crypto_hash(); + let mut diff = [0u8; 32]; + raw_header.difficulty.to_big_endian(&mut diff); + let extend_and_nonce = [0u8; 12]; + blob.extend_from_slice(raw_header_hash.to_vec().as_slice()); + blob.extend_from_slice(&extend_and_nonce); + blob.extend_from_slice(&diff); + blob + } + + pub fn id(&self) -> HashValue { + self.id + .expect("DagBlockHeader id should be Some after init.") + } + + pub fn parent_hash(&self) -> Vec { + self.parent_hash.clone() + } + + pub fn timestamp(&self) -> u64 { + self.timestamp + } + + pub fn author(&self) -> AccountAddress { + self.author + } + + pub fn author_auth_key(&self) -> Option { + self.author_auth_key + } + + pub fn txn_accumulator_root(&self) -> HashValue { + self.txn_accumulator_root + } + + pub fn state_root(&self) -> HashValue { + self.state_root + } + + pub fn gas_used(&self) -> u64 { + self.gas_used + } + + pub fn nonce(&self) -> u32 { + self.nonce + } + + pub fn difficulty(&self) -> U256 { + self.difficulty + } + + pub fn block_accumulator_root(&self) -> HashValue { + self.block_accumulator_root + } + + pub fn body_hash(&self) -> HashValue { + self.body_hash + } + + pub fn chain_id(&self) -> ChainId { + self.chain_id + } + + pub fn extra(&self) -> &BlockHeaderExtra { + &self.extra + } + + pub fn is_genesis(&self) -> bool { + if self.parent_hash.len() == 1 { + return self.parent_hash[0] == HashValue::new(ORIGIN); + } + false + } + + pub fn genesis_block_header( + parent_hash: Vec, + timestamp: u64, + txn_accumulator_root: HashValue, + state_root: HashValue, + difficulty: U256, + body_hash: HashValue, + chain_id: ChainId, + ) -> Self { + Self::new( + parent_hash, + timestamp, + CORE_CODE_ADDRESS, + txn_accumulator_root, + *ACCUMULATOR_PLACEHOLDER_HASH, + state_root, + 0, + difficulty, + body_hash, + chain_id, + 0, + BlockHeaderExtra::default(), + ) + } + + pub fn random() -> Self { + Self::new( + vec![HashValue::random()], + rand::random(), + AccountAddress::random(), + HashValue::random(), + HashValue::random(), + HashValue::random(), + rand::random(), + U256::max_value(), + HashValue::random(), + ChainId::test(), + 0, + BlockHeaderExtra::new([0u8; 4]), + ) + } + + pub fn as_builder(&self) -> DagBlockHeaderBuilder { + DagBlockHeaderBuilder::new_with(self.clone()) + } +} + +impl<'de> Deserialize<'de> for DagBlockHeader { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(rename = "DagBlockHeader")] + struct DagBlockHeaderData { + parent_hash: Vec, + timestamp: u64, + author: AccountAddress, + author_auth_key: Option, + txn_accumulator_root: HashValue, + block_accumulator_root: HashValue, + state_root: HashValue, + gas_used: u64, + difficulty: U256, + body_hash: HashValue, + chain_id: ChainId, + nonce: u32, + extra: BlockHeaderExtra, + } + + let header_data = DagBlockHeaderData::deserialize(deserializer)?; + let block_header = Self::new_with_auth_key( + header_data.parent_hash, + header_data.timestamp, + header_data.author, + header_data.author_auth_key, + header_data.txn_accumulator_root, + header_data.block_accumulator_root, + header_data.state_root, + header_data.gas_used, + header_data.difficulty, + header_data.body_hash, + header_data.chain_id, + header_data.nonce, + header_data.extra, + ); + Ok(block_header) + } +} + +impl Default for DagBlockHeader { + fn default() -> Self { + Self::new( + vec![HashValue::zero()], + 0, + AccountAddress::ZERO, + HashValue::zero(), + HashValue::zero(), + HashValue::zero(), + 0, + 0.into(), + HashValue::zero(), + ChainId::test(), + 0, + BlockHeaderExtra::new([0u8; 4]), + ) + } +} + +impl Sample for DagBlockHeader { + fn sample() -> Self { + Self::new( + vec![HashValue::zero()], + 1610110515000, + genesis_address(), + *ACCUMULATOR_PLACEHOLDER_HASH, + *ACCUMULATOR_PLACEHOLDER_HASH, + *SPARSE_MERKLE_PLACEHOLDER_HASH, + 0, + U256::from(1), + BlockBody::sample().crypto_hash(), + ChainId::test(), + 0, + BlockHeaderExtra::new([0u8; 4]), + ) + } +} + +#[allow(clippy::from_over_into)] +impl Into for DagBlockHeader { + fn into(self) -> RawDagBlockHeader { + RawDagBlockHeader { + parent_hash: self.parent_hash, + timestamp: self.timestamp, + author: self.author, + author_auth_key: self.author_auth_key, + accumulator_root: self.txn_accumulator_root, + parent_block_accumulator_root: self.block_accumulator_root, + state_root: self.state_root, + gas_used: self.gas_used, + difficulty: self.difficulty, + body_hash: self.body_hash, + chain_id: self.chain_id, + } + } +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash)] +pub struct RawDagBlockHeader { + /// Parent hash. + pub parent_hash: Vec, + /// Block timestamp. + pub timestamp: u64, + /// Block author. + pub author: AccountAddress, + /// Block author auth key. + /// this field is deprecated + pub author_auth_key: Option, + /// The transaction accumulator root hash after executing this block. + pub accumulator_root: HashValue, + /// The parent block accumulator root hash. + pub parent_block_accumulator_root: HashValue, + /// The last transaction state_root of this block after execute. + pub state_root: HashValue, + /// Gas used for contracts execution. + pub gas_used: u64, + /// Block difficulty + pub difficulty: U256, + /// hash for block body + pub body_hash: HashValue, + /// The chain id + pub chain_id: ChainId, +} + +#[derive(Default)] +pub struct DagBlockHeaderBuilder { + buffer: DagBlockHeader, +} + +impl DagBlockHeaderBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn random() -> Self { + Self { + buffer: DagBlockHeader::random(), + } + } + + fn new_with(buffer: DagBlockHeader) -> Self { + Self { buffer } + } + + pub fn with_parent_hash(mut self, parent_hash: Vec) -> Self { + self.buffer.parent_hash = parent_hash; + self + } + + pub fn with_timestamp(mut self, timestamp: u64) -> Self { + self.buffer.timestamp = timestamp; + self + } + + pub fn with_author(mut self, author: AccountAddress) -> Self { + self.buffer.author = author; + self + } + + pub fn with_author_auth_key(mut self, author_auth_key: Option) -> Self { + self.buffer.author_auth_key = author_auth_key; + self + } + + pub fn with_accumulator_root(mut self, accumulator_root: HashValue) -> Self { + self.buffer.txn_accumulator_root = accumulator_root; + self + } + + pub fn with_parent_block_accumulator_root( + mut self, + parent_block_accumulator_root: HashValue, + ) -> Self { + self.buffer.block_accumulator_root = parent_block_accumulator_root; + self + } + + pub fn with_state_root(mut self, state_root: HashValue) -> Self { + self.buffer.state_root = state_root; + self + } + + pub fn with_gas_used(mut self, gas_used: u64) -> Self { + self.buffer.gas_used = gas_used; + self + } + + pub fn with_difficulty(mut self, difficulty: U256) -> Self { + self.buffer.difficulty = difficulty; + self + } + + pub fn with_body_hash(mut self, body_hash: HashValue) -> Self { + self.buffer.body_hash = body_hash; + self + } + + pub fn with_chain_id(mut self, chain_id: ChainId) -> Self { + self.buffer.chain_id = chain_id; + self + } + + pub fn with_nonce(mut self, nonce: u32) -> Self { + self.buffer.nonce = nonce; + self + } + + pub fn with_extra(mut self, extra: BlockHeaderExtra) -> Self { + self.buffer.extra = extra; + self + } + + pub fn build(mut self) -> DagBlockHeader { + self.buffer.id = Some(self.buffer.crypto_hash()); + self.buffer + } +} + +#[derive( + Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, +)] +pub struct BlockBody { + /// The transactions in this block. + pub transactions: Vec, + /// uncles block header + pub uncles: Option>, +} + +impl BlockBody { + pub fn new( + transactions: Vec, + uncles: Option>, + ) -> Self { + Self { + transactions, + uncles, + } + } + pub fn get_txn(&self, index: usize) -> Option<&SignedUserTransaction> { + self.transactions.get(index) + } + + /// Just for test + pub fn new_empty() -> BlockBody { + BlockBody { + transactions: Vec::new(), + uncles: None, + } + } + + pub fn hash(&self) -> HashValue { + self.crypto_hash() + } +} + +#[allow(clippy::from_over_into)] +impl Into for Vec { + fn into(self) -> BlockBody { + BlockBody { + transactions: self, + uncles: None, + } + } +} + +#[allow(clippy::from_over_into)] +impl Into> for BlockBody { + fn into(self) -> Vec { + self.transactions + } +} + +impl Sample for BlockBody { + fn sample() -> Self { + Self { + transactions: vec![], + uncles: None, + } + } +} + +/// A block, encoded as it is on the block chain. +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash)] +pub struct Block { + /// The header of this block. + pub header: DagBlockHeader, + /// The body of this block. + pub body: BlockBody, +} + +impl Block { + pub fn new(header: DagBlockHeader, body: B) -> Self + where + B: Into, + { + Block { + header, + body: body.into(), + } + } + + pub fn id(&self) -> HashValue { + self.header.id() + } + pub fn header(&self) -> &DagBlockHeader { + &self.header + } + pub fn transactions(&self) -> &[SignedUserTransaction] { + self.body.transactions.as_slice() + } + + pub fn uncles(&self) -> Option<&[DagBlockHeader]> { + match &self.body.uncles { + Some(uncles) => Some(uncles.as_slice()), + None => None, + } + } + + pub fn uncle_ids(&self) -> Vec { + self.uncles() + .map(|uncles| uncles.iter().map(|header| header.id()).collect()) + .unwrap_or_default() + } + + pub fn into_inner(self) -> (DagBlockHeader, BlockBody) { + (self.header, self.body) + } + + pub fn genesis_block( + parent_hash: Vec, + timestamp: u64, + accumulator_root: HashValue, + state_root: HashValue, + difficulty: U256, + genesis_txn: SignedUserTransaction, + ) -> Self { + let chain_id = genesis_txn.chain_id(); + let block_body = BlockBody::new(vec![genesis_txn], None); + let header = DagBlockHeader::genesis_block_header( + parent_hash, + timestamp, + accumulator_root, + state_root, + difficulty, + block_body.hash(), + chain_id, + ); + Self { + header, + body: block_body, + } + } + + pub fn to_metadata(&self, parent_gas_used: u64) -> DagBlockMetadata { + DagBlockMetadata::new( + self.header.parent_hash(), + self.header.timestamp, + self.header.author, + self.header.author_auth_key, + self.header.chain_id, + parent_gas_used, + ) + } +} + +impl std::fmt::Display for Block { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Block{{id:\"{}\", parent_id:\"{:?}\",", + self.id(), + self.header().parent_hash() + )?; + if let Some(uncles) = &self.body.uncles { + write!(f, "uncles:[")?; + for uncle in uncles { + write!(f, "\"{}\",", uncle.id())?; + } + write!(f, "],")?; + } + write!(f, "transactions:[")?; + for txn in &self.body.transactions { + write!(f, "\"{}\",", txn.id())?; + } + write!(f, "]}}") + } +} + +impl Sample for Block { + fn sample() -> Self { + Self { + header: DagBlockHeader::sample(), + body: BlockBody::sample(), + } + } +} + +/// `BlockInfo` is the object we store in the storage. It consists of the +/// block as well as the execution result of this block. +#[derive( + Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, JsonSchema, +)] +pub struct BlockInfo { + /// Block id + pub block_id: HashValue, + /// The total difficulty. + #[schemars(with = "String")] + pub total_difficulty: U256, + /// The transaction accumulator info + pub txn_accumulator_info: AccumulatorInfo, + /// The block accumulator info. + pub block_accumulator_info: AccumulatorInfo, +} + +impl BlockInfo { + pub fn new( + block_id: HashValue, + total_difficulty: U256, + txn_accumulator_info: AccumulatorInfo, + block_accumulator_info: AccumulatorInfo, + ) -> Self { + Self { + block_id, + total_difficulty, + txn_accumulator_info, + block_accumulator_info, + } + } + + pub fn id(&self) -> HashValue { + self.crypto_hash() + } + + pub fn get_total_difficulty(&self) -> U256 { + self.total_difficulty + } + + pub fn get_block_accumulator_info(&self) -> &AccumulatorInfo { + &self.block_accumulator_info + } + + pub fn get_txn_accumulator_info(&self) -> &AccumulatorInfo { + &self.txn_accumulator_info + } + + pub fn block_id(&self) -> &HashValue { + &self.block_id + } +} + +impl Sample for BlockInfo { + fn sample() -> Self { + Self { + block_id: DagBlockHeader::sample().id(), + total_difficulty: 0.into(), + txn_accumulator_info: AccumulatorInfo::sample(), + block_accumulator_info: AccumulatorInfo::sample(), + } + } +} + +#[derive(Clone, Debug)] +pub struct DagBlockTemplate { + /// Parent hash. + pub parent_hash: Vec, + /// Block timestamp. + pub timestamp: u64, + /// Block author. + pub author: AccountAddress, + /// The transaction accumulator root hash after executing this block. + pub txn_accumulator_root: HashValue, + /// The block accumulator root hash. + pub block_accumulator_root: HashValue, + /// The last transaction state_root of this block after execute. + pub state_root: HashValue, + /// Gas used for contracts execution. + pub gas_used: u64, + /// hash for block body + pub body_hash: HashValue, + /// body of the block + pub body: BlockBody, + /// The chain id + pub chain_id: ChainId, + /// Block difficulty + pub difficulty: U256, + /// Block consensus strategy + pub strategy: ConsensusStrategy, +} + +impl DagBlockTemplate { + pub fn new( + parent_block_accumulator_root: HashValue, + accumulator_root: HashValue, + state_root: HashValue, + gas_used: u64, + body: BlockBody, + chain_id: ChainId, + difficulty: U256, + strategy: ConsensusStrategy, + block_metadata: DagBlockMetadata, + ) -> Self { + let (parent_hash, timestamp, author, _author_auth_key, _, _) = block_metadata.into_inner(); + Self { + parent_hash, + block_accumulator_root: parent_block_accumulator_root, + timestamp, + author, + txn_accumulator_root: accumulator_root, + state_root, + gas_used, + body_hash: body.hash(), + body, + chain_id, + difficulty, + strategy, + } + } + + pub fn into_block(self, nonce: u32, extra: BlockHeaderExtra) -> Block { + let header = DagBlockHeader::new( + self.parent_hash, + self.timestamp, + self.author, + self.txn_accumulator_root, + self.block_accumulator_root, + self.state_root, + self.gas_used, + self.difficulty, + self.body_hash, + self.chain_id, + nonce, + extra, + ); + Block { + header, + body: self.body, + } + } + + pub fn as_raw_block_header(&self) -> RawDagBlockHeader { + RawDagBlockHeader { + parent_hash: self.parent_hash.clone(), + timestamp: self.timestamp, + author: self.author, + author_auth_key: None, + accumulator_root: self.txn_accumulator_root, + parent_block_accumulator_root: self.block_accumulator_root, + state_root: self.state_root, + gas_used: self.gas_used, + body_hash: self.body_hash, + difficulty: self.difficulty, + chain_id: self.chain_id, + } + } + + pub fn as_pow_header_blob(&self) -> Vec { + let mut blob = Vec::new(); + let raw_header = self.as_raw_block_header(); + let raw_header_hash = raw_header.crypto_hash(); + let mut dh = [0u8; 32]; + raw_header.difficulty.to_big_endian(&mut dh); + let extend_and_nonce = [0u8; 12]; + + blob.extend_from_slice(raw_header_hash.to_vec().as_slice()); + blob.extend_from_slice(&extend_and_nonce); + blob.extend_from_slice(&dh); + blob + } + + pub fn into_block_header(self, nonce: u32, extra: BlockHeaderExtra) -> DagBlockHeader { + DagBlockHeader::new( + self.parent_hash, + self.timestamp, + self.author, + self.txn_accumulator_root, + self.block_accumulator_root, + self.state_root, + self.gas_used, + self.difficulty, + self.body_hash, + self.chain_id, + nonce, + extra, + ) + } +} + +#[derive(Clone, Debug, Hash, Serialize, Deserialize, CryptoHasher, CryptoHash)] +pub struct ExecutedBlock { + pub block: Block, + pub block_info: BlockInfo, +} + +impl ExecutedBlock { + pub fn new(block: Block, block_info: BlockInfo) -> Self { + ExecutedBlock { block, block_info } + } + + pub fn total_difficulty(&self) -> U256 { + self.block_info.total_difficulty + } + + pub fn block(&self) -> &Block { + &self.block + } + + pub fn block_info(&self) -> &BlockInfo { + &self.block_info + } + + pub fn header(&self) -> &DagBlockHeader { + self.block.header() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BlockSummary { + pub block_header: DagBlockHeader, + pub uncles: Vec, +} + +impl BlockSummary { + pub fn uncles(&self) -> &[DagBlockHeader] { + &self.uncles + } + + pub fn header(&self) -> &DagBlockHeader { + &self.block_header + } +} + +impl From for BlockSummary { + fn from(block: Block) -> Self { + Self { + block_header: block.header, + uncles: block.body.uncles.unwrap_or_default(), + } + } +} + +#[allow(clippy::from_over_into)] +impl Into<(DagBlockHeader, Vec)> for BlockSummary { + fn into(self) -> (DagBlockHeader, Vec) { + (self.block_header, self.uncles) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UncleSummary { + /// total uncle + pub uncles: u64, + /// sum(number of the block which contain uncle block - uncle parent block number). + pub sum: u64, + pub avg: u64, + pub time_sum: u64, + pub time_avg: u64, +} + +impl UncleSummary { + pub fn new(uncles: u64, sum: u64, time_sum: u64) -> Self { + let (avg, time_avg) = ( + sum.checked_div(uncles).unwrap_or_default(), + time_sum.checked_div(uncles).unwrap_or_default(), + ); + Self { + uncles, + sum, + avg, + time_sum, + time_avg, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EpochUncleSummary { + /// epoch number + pub epoch: u64, + pub number_summary: UncleSummary, + pub epoch_summary: UncleSummary, +} + +impl EpochUncleSummary { + pub fn new(epoch: u64, number_summary: UncleSummary, epoch_summary: UncleSummary) -> Self { + Self { + epoch, + number_summary, + epoch_summary, + } + } +} diff --git a/types/src/header.rs b/types/src/header.rs index 8c5dcb591b..438bef47f0 100644 --- a/types/src/header.rs +++ b/types/src/header.rs @@ -13,25 +13,33 @@ pub trait ConsensusHeader { } #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct Header { +pub struct DagHeader { block_header: BlockHeader, parents_hash: Vec, } -impl Header { +impl DagHeader { pub fn new(block_header: BlockHeader, parents_hash: Vec) -> Self { Self { block_header, parents_hash, } } + pub fn new_genesis(genesis_header: BlockHeader) -> DagHeader { + Self { + block_header: genesis_header, + parents_hash: vec![Hash::new(ORIGIN)], + } + } +} - pub fn genesis_hash(&self) -> Hash { - Hash::new(ORIGIN) +impl Into for DagHeader { + fn into(self) -> BlockHeader { + self.block_header } } -impl ConsensusHeader for Header { +impl ConsensusHeader for DagHeader { fn parents_hash(&self) -> &[Hash] { &self.parents_hash } @@ -49,7 +57,7 @@ impl ConsensusHeader for Header { #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct HeaderWithBlockLevel { - pub header: Arc

, + pub header: Arc, pub block_level: BlockLevel, } diff --git a/types/src/lib.rs b/types/src/lib.rs index 7e4e65b6d8..9ff354a624 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -24,6 +24,7 @@ pub mod account_state; #[allow(clippy::too_many_arguments)] pub mod block; pub mod compact_block; +pub mod dag_block; pub mod block_metadata { pub use starcoin_vm_types::block_metadata::BlockMetadata; diff --git a/types/src/system_events.rs b/types/src/system_events.rs index d8fd2ce137..481ddd6abf 100644 --- a/types/src/system_events.rs +++ b/types/src/system_events.rs @@ -13,12 +13,11 @@ use std::sync::Arc; pub struct NewHeadBlock( pub Arc, pub Option>, - pub Option>, ); /// may be uncle block #[derive(Clone, Debug)] -pub struct NewBranch(pub Arc, pub Option>); +pub struct NewBranch(pub Arc,pub Option>); #[derive(Clone, Debug)] pub struct MinedBlock(pub Arc, pub Option>); From 1a6ca3f24f4da614a124943c11bde151cb6a923c Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Tue, 17 Oct 2023 16:23:08 +0800 Subject: [PATCH 09/17] fix compile --- Cargo.lock | 46 +-- Cargo.toml | 2 +- chain/chain-notify/src/lib.rs | 2 +- cmd/db-exporter/src/main.rs | 12 +- node/src/node.rs | 2 +- state/service/src/service.rs | 2 +- storage/src/lib.rs | 5 +- .../block_connector_service.rs | 4 +- sync/src/block_connector/write_block_chain.rs | 54 +-- sync/src/sync.rs | 2 +- sync/src/tasks/block_sync_task.rs | 137 ++++---- sync/src/tasks/inner_sync_task.rs | 3 +- sync/src/tasks/mod.rs | 2 +- sync/src/tasks/sync_dag_full_task.rs | 10 +- vm/compiler/Cargo.toml | 2 +- vm/dev/Cargo.toml | 2 +- vm/gas-algebra-ext/Cargo.toml | 2 +- vm/move-coverage/Cargo.toml | 2 +- vm/move-explain/Cargo.toml | 2 +- vm/move-package-manager/Cargo.toml | 2 +- vm/move-package-manager/src/release.rs | 3 +- vm/move-prover/Cargo.toml | 2 +- vm/natives/Cargo.toml | 2 +- vm/resource-viewer/Cargo.toml | 2 +- vm/starcoin-gas/Cargo.toml | 2 +- .../Cargo.toml | 2 +- .../src/fork_chain.rs | 10 +- .../src/fork_state.rs | 11 +- .../src/lib.rs | 10 +- .../tests/cases/call_api_cmd.exp | 2 +- .../tests/cases/call_api_cmd_halley.exp | 2 +- vm/stdlib/Cargo.toml | 2 +- vm/stdlib/compiled/12/11-12/stdlib.blob | Bin 111158 -> 113341 bytes .../compiled/12/11-12/stdlib/008_Vector.mv | Bin 1240 -> 1256 bytes vm/stdlib/compiled/12/11-12/stdlib/010_ACL.mv | Bin 465 -> 435 bytes .../compiled/12/11-12/stdlib/012_Math.mv | Bin 746 -> 688 bytes .../compiled/12/11-12/stdlib/013_Option.mv | Bin 1136 -> 1051 bytes vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv | Bin 3135 -> 3074 bytes .../compiled/12/11-12/stdlib/021_VMConfig.mv | Bin 4003 -> 3967 bytes .../12/11-12/stdlib/036_Authenticator.mv | Bin 873 -> 801 bytes .../compiled/12/11-12/stdlib/037_Account.mv | Bin 6300 -> 6890 bytes .../compiled/12/11-12/stdlib/040_Ring.mv | Bin 1418 -> 1292 bytes .../12/11-12/stdlib/043_BlockReward.mv | Bin 1593 -> 1514 bytes .../12/11-12/stdlib/044_Collection.mv | Bin 842 -> 814 bytes .../12/11-12/stdlib/045_Collection2.mv | Bin 1924 -> 1860 bytes .../compiled/12/11-12/stdlib/046_Compare.mv | Bin 682 -> 623 bytes .../12/11-12/stdlib/051_EVMAddress.mv | Bin 476 -> 400 bytes vm/stdlib/compiled/12/stdlib/008_Vector.mv | Bin 1240 -> 1256 bytes vm/stdlib/compiled/12/stdlib/010_ACL.mv | Bin 465 -> 435 bytes vm/stdlib/compiled/12/stdlib/012_Math.mv | Bin 746 -> 688 bytes vm/stdlib/compiled/12/stdlib/013_Option.mv | Bin 1136 -> 1051 bytes vm/stdlib/compiled/12/stdlib/014_BCS.mv | Bin 3135 -> 3074 bytes vm/stdlib/compiled/12/stdlib/021_VMConfig.mv | Bin 4003 -> 3967 bytes .../compiled/12/stdlib/036_Authenticator.mv | Bin 873 -> 801 bytes vm/stdlib/compiled/12/stdlib/037_Account.mv | Bin 6300 -> 6890 bytes vm/stdlib/compiled/12/stdlib/040_Ring.mv | Bin 1418 -> 1292 bytes .../compiled/12/stdlib/043_BlockReward.mv | Bin 1593 -> 1514 bytes .../compiled/12/stdlib/044_Collection.mv | Bin 842 -> 814 bytes .../compiled/12/stdlib/045_Collection2.mv | Bin 1924 -> 1860 bytes vm/stdlib/compiled/12/stdlib/046_Compare.mv | Bin 682 -> 623 bytes .../compiled/12/stdlib/051_EVMAddress.mv | Bin 476 -> 400 bytes .../error_descriptions.errmap | Bin 9962 -> 10372 bytes .../compiled/latest/stdlib/008_Vector.mv | Bin 1240 -> 1256 bytes vm/stdlib/compiled/latest/stdlib/010_ACL.mv | Bin 465 -> 435 bytes vm/stdlib/compiled/latest/stdlib/012_Math.mv | Bin 746 -> 688 bytes .../compiled/latest/stdlib/013_Option.mv | Bin 1136 -> 1051 bytes vm/stdlib/compiled/latest/stdlib/014_BCS.mv | Bin 3135 -> 3074 bytes .../compiled/latest/stdlib/021_VMConfig.mv | Bin 4003 -> 3967 bytes .../latest/stdlib/036_Authenticator.mv | Bin 873 -> 801 bytes .../compiled/latest/stdlib/037_Account.mv | Bin 6300 -> 6890 bytes vm/stdlib/compiled/latest/stdlib/040_Ring.mv | Bin 1418 -> 1292 bytes .../compiled/latest/stdlib/043_BlockReward.mv | Bin 1593 -> 1514 bytes .../compiled/latest/stdlib/044_Collection.mv | Bin 842 -> 814 bytes .../compiled/latest/stdlib/045_Collection2.mv | Bin 1924 -> 1860 bytes .../compiled/latest/stdlib/046_Compare.mv | Bin 682 -> 623 bytes .../compiled/latest/stdlib/051_EVMAddress.mv | Bin 476 -> 400 bytes vm/transaction-builder-generator/Cargo.toml | 2 +- vm/transaction-builder/Cargo.toml | 3 +- vm/transaction-builder/src/lib.rs | 327 +++++++++--------- vm/types/Cargo.toml | 2 +- .../src/account_config/constants/addresses.rs | 36 +- vm/types/src/on_chain_config/gas_schedule.rs | 2 - vm/types/src/on_chain_config/mod.rs | 1 - vm/types/src/proptest_types.rs | 2 +- vm/types/src/state_store/state_key.rs | 3 - vm/types/src/state_store/table.rs | 36 +- vm/types/src/transaction/mod.rs | 35 +- vm/vm-runtime/Cargo.toml | 25 +- vm/vm-runtime/src/data_cache.rs | 1 + vm/vm-runtime/src/lib.rs | 34 +- vm/vm-runtime/src/move_vm_ext/session.rs | 21 +- vm/vm-runtime/src/starcoin_vm.rs | 223 ++++++++---- vm/vm-status-translator/Cargo.toml | 2 +- 93 files changed, 665 insertions(+), 435 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1196f516e4..003d2bd61e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2315,7 +2315,7 @@ checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" [[package]] name = "diem-crypto" version = "0.0.3" -source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=a742ddc0674022800341182cbb4c3681807b2f00#a742ddc0674022800341182cbb4c3681807b2f00" +source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=8d41c280a227594ca0a2b6ecba580643518274ea#8d41c280a227594ca0a2b6ecba580643518274ea" dependencies = [ "aes-gcm 0.8.0", "anyhow", @@ -2350,7 +2350,7 @@ dependencies = [ [[package]] name = "diem-crypto-derive" version = "0.0.3" -source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=a742ddc0674022800341182cbb4c3681807b2f00#a742ddc0674022800341182cbb4c3681807b2f00" +source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=8d41c280a227594ca0a2b6ecba580643518274ea#8d41c280a227594ca0a2b6ecba580643518274ea" dependencies = [ "proc-macro2 1.0.59", "quote 1.0.28", @@ -5258,7 +5258,7 @@ dependencies = [ [[package]] name = "move-coverage" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs", @@ -5452,7 +5452,7 @@ dependencies = [ [[package]] name = "move-package-manager" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs", @@ -9532,7 +9532,7 @@ dependencies = [ [[package]] name = "starcoin-crypto" version = "1.10.0-rc.2" -source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=a742ddc0674022800341182cbb4c3681807b2f00#a742ddc0674022800341182cbb4c3681807b2f00" +source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=8d41c280a227594ca0a2b6ecba580643518274ea#8d41c280a227594ca0a2b6ecba580643518274ea" dependencies = [ "anyhow", "bcs", @@ -9551,7 +9551,7 @@ dependencies = [ [[package]] name = "starcoin-crypto-macro" version = "1.10.0-rc.2" -source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=a742ddc0674022800341182cbb4c3681807b2f00#a742ddc0674022800341182cbb4c3681807b2f00" +source = "git+https://github.com/starcoinorg/starcoin-crypto?rev=8d41c280a227594ca0a2b6ecba580643518274ea#8d41c280a227594ca0a2b6ecba580643518274ea" dependencies = [ "proc-macro2 1.0.59", "quote 1.0.28", @@ -9588,7 +9588,7 @@ dependencies = [ [[package]] name = "starcoin-dev" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs-ext", @@ -9707,7 +9707,7 @@ dependencies = [ [[package]] name = "starcoin-gas" -version = "1.13.5" +version = "1.13.7" dependencies = [ "clap 3.2.23", "move-binary-format", @@ -9722,7 +9722,7 @@ dependencies = [ [[package]] name = "starcoin-gas-algebra-ext" -version = "1.13.5" +version = "1.13.7" dependencies = [ "move-binary-format", "move-core-types", @@ -9977,7 +9977,7 @@ dependencies = [ [[package]] name = "starcoin-move-compiler" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "move-binary-format", @@ -9996,7 +9996,7 @@ dependencies = [ [[package]] name = "starcoin-move-explain" -version = "1.13.5" +version = "1.13.7" dependencies = [ "bcs-ext", "clap 3.2.23", @@ -10006,7 +10006,7 @@ dependencies = [ [[package]] name = "starcoin-move-prover" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "atty", @@ -10048,7 +10048,7 @@ dependencies = [ [[package]] name = "starcoin-natives" -version = "1.13.5" +version = "1.13.7" dependencies = [ "arrayref", "hex", @@ -10352,7 +10352,7 @@ dependencies = [ [[package]] name = "starcoin-resource-viewer" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "hex", @@ -10827,13 +10827,14 @@ dependencies = [ [[package]] name = "starcoin-transaction-builder" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs-ext", "starcoin-config", "starcoin-crypto", "starcoin-logger", + "starcoin-types", "starcoin-vm-types", "stdlib", "stest", @@ -10841,7 +10842,7 @@ dependencies = [ [[package]] name = "starcoin-transactional-test-harness" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "async-trait", @@ -11024,7 +11025,7 @@ dependencies = [ [[package]] name = "starcoin-vm-runtime" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs-ext", @@ -11032,10 +11033,12 @@ dependencies = [ "move-stdlib", "move-table-extension", "move-vm-runtime", + "num_cpus", "num_enum", "once_cell", "rand 0.8.5", "rand_core 0.6.4", + "rayon", "serde 1.0.152", "starcoin-config", "starcoin-crypto", @@ -11044,6 +11047,7 @@ dependencies = [ "starcoin-logger", "starcoin-metrics", "starcoin-natives", + "starcoin-parallel-executor", "starcoin-types", "starcoin-vm-types", "stdlib", @@ -11052,7 +11056,7 @@ dependencies = [ [[package]] name = "starcoin-vm-types" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs-ext", @@ -11091,7 +11095,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stdlib" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs-ext", @@ -12057,7 +12061,7 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "transaction-builder-generator" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "bcs", @@ -12502,7 +12506,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vm-status-translator" -version = "1.13.5" +version = "1.13.7" dependencies = [ "anyhow", "schemars", diff --git a/Cargo.toml b/Cargo.toml index fc7a208a3d..2bc809a2d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -441,7 +441,7 @@ starcoin-chain-service = { path = "chain/service" } starcoin-cmd = { path = "cmd/starcoin" } starcoin-config = { path = "config" } starcoin-consensus = { path = "consensus" } -starcoin-crypto = { git = "https://github.com/starcoinorg/starcoin-crypto", rev = "a742ddc0674022800341182cbb4c3681807b2f00" } +starcoin-crypto = { git = "https://github.com/starcoinorg/starcoin-crypto", rev = "8d41c280a227594ca0a2b6ecba580643518274ea" } starcoin-decrypt = { path = "commons/decrypt" } starcoin-dev = { path = "vm/dev" } starcoin-executor = { path = "executor" } diff --git a/chain/chain-notify/src/lib.rs b/chain/chain-notify/src/lib.rs index c606e6ddf9..de3f0900e5 100644 --- a/chain/chain-notify/src/lib.rs +++ b/chain/chain-notify/src/lib.rs @@ -52,7 +52,7 @@ impl EventHandler for ChainNotifyHandlerService { item: NewHeadBlock, ctx: &mut ServiceContext, ) { - let NewHeadBlock(block_detail,tips_hash) = item; + let NewHeadBlock(block_detail, _tips_hash) = item; let block = block_detail.block(); // notify header. self.notify_new_block(block, ctx); diff --git a/cmd/db-exporter/src/main.rs b/cmd/db-exporter/src/main.rs index 220a7e6879..4e5f7a44fd 100644 --- a/cmd/db-exporter/src/main.rs +++ b/cmd/db-exporter/src/main.rs @@ -1154,7 +1154,7 @@ pub fn execute_turbo_stm_transaction_with_fixed_account( ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; println!("create account trans {}", block.transactions().len()); let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; println!("receivers finish"); @@ -1181,7 +1181,7 @@ pub fn execute_turbo_stm_transaction_with_fixed_account( ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; println!("p2p trans {}", block.transactions().len()); let block_hash = block.header.id(); - chain.apply_with_verifier::(block)?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -2028,14 +2028,14 @@ pub fn apply_turbo_stm_block( println!("seq execution"); for item in blocks.iter().take(4) { - chain_seq.apply_with_verifier::(item.clone())?; + chain_seq.apply_with_verifier::(item.clone(), None)?; } let mut block_hash = HashValue::zero(); let start_time = SystemTime::now(); for item in blocks.iter().skip(4) { let block = item.clone(); block_hash = block.header().id(); - chain_seq.apply_with_verifier::(block)?; + chain_seq.apply_with_verifier::(block, None)?; } let startup_info = StartupInfo::new(block_hash); storage_seq.save_startup_info(startup_info)?; @@ -2063,7 +2063,7 @@ pub fn apply_turbo_stm_block( println!("stm execution"); for item in blocks.iter().take(4) { - chain_stm.apply_with_verifier::(item.clone())?; + chain_stm.apply_with_verifier::(item.clone(), None)?; } let mut block_hash = HashValue::zero(); let start_time = SystemTime::now(); @@ -2071,7 +2071,7 @@ pub fn apply_turbo_stm_block( for item in blocks.iter().skip(4) { let block = item.clone(); block_hash = block.header().id(); - chain_stm.apply_with_verifier::(block)?; + chain_stm.apply_with_verifier::(block, None)?; } let startup_info = StartupInfo::new(block_hash); storage_stm.save_startup_info(startup_info)?; diff --git a/node/src/node.rs b/node/src/node.rs index b432ac84a3..99a03b93a9 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -138,7 +138,7 @@ impl ServiceHandler for NodeService { .start_service_sync(GenerateBlockEventPacemaker::service_name()), ), NodeRequest::ResetNode(block_hash) => { - let connect_service = ctx.service_ref::()?.clone(); + let connect_service = ctx.service_ref::>()?.clone(); let dag = ctx .get_shared::>() .expect("ghost dag object does not exits"); diff --git a/state/service/src/service.rs b/state/service/src/service.rs index b6746a9b8e..7c033860aa 100644 --- a/state/service/src/service.rs +++ b/state/service/src/service.rs @@ -131,7 +131,7 @@ impl ServiceHandler for ChainStateService { impl EventHandler for ChainStateService { fn handle_event(&mut self, msg: NewHeadBlock, _ctx: &mut ServiceContext) { - let NewHeadBlock(block, _dag_parents, _next_tips) = msg; + let NewHeadBlock(block, _dag_parents) = msg; let state_root = block.header().state_root(); debug!("ChainStateActor change StateRoot to : {:?}", state_root); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index a027b3fa57..aabb151477 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -33,7 +33,8 @@ use starcoin_types::{ startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo}, transaction::{RichTransactionInfo, Transaction}, }; -//use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; +use starcoin_vm_types::{state_store::table::{TableInfo, TableHandle}, account_address::AccountAddress}; +use table_info::{TableInfoStore, TableInfoStorage}; use std::{ collections::BTreeMap, fmt::{Debug, Display, Formatter}, @@ -358,7 +359,7 @@ impl Storage { block_info_storage: BlockInfoStorage::new(instance.clone()), event_storage: ContractEventStorage::new(instance.clone()), chain_info_storage: ChainInfoStorage::new(instance.clone()), - flexi_dag_storage: SyncFlexiDagStorage::new(instance), + flexi_dag_storage: SyncFlexiDagStorage::new(instance.clone()), table_info_storage: TableInfoStorage::new(instance), // instance, }; diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index 1f9b209a96..3a1e1f8216 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -239,7 +239,7 @@ impl EventHandler for BlockConnectorService { - if let Err(e) = self.chain_service.apply_failed(block) { + if let Err(e) = self.chain_service.apply_failed(block, msg.dag_parents) { error!("Process connected new block from sync error: {:?}", e); } } @@ -380,7 +380,7 @@ where _ctx: &mut ServiceContext>, ) -> Result { self.chain_service - .execute(msg.block, msg.dag_transaction_parent) + .execute(msg.block, msg.dag_block_parent) } } diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 8fd70aecc1..9e6021b7c2 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -20,6 +20,7 @@ use starcoin_time_service::{DagBlockTimeWindowService, TimeWindowResult}; use starcoin_txpool_api::TxPoolSyncService; use starcoin_types::block::BlockInfo; use starcoin_types::blockhash::BlockHashMap; +use starcoin_types::header::DagHeader; use starcoin_types::{ block::{Block, BlockHeader, ExecutedBlock}, startup_info::StartupInfo, @@ -74,7 +75,7 @@ impl ConnectOk { ConnectOk::ExeConnectMain(block) => Some(block.clone()), ConnectOk::ExeConnectBranch(block) => Some(block.clone()), ConnectOk::Connect(block) => Some(block.clone()), - ConnectOk::DagConnected | ConnectOk::MainDuplicate | ConnectOk::DagPending => None, + ConnectOk::DagConnected | ConnectOk::MainDuplicate | ConnectOk::DagPending | ConnectOk::DagConnectMissingBlock => None, } } } @@ -89,6 +90,7 @@ impl std::fmt::Display for ConnectOk { ConnectOk::DagConnected => "DagConnect", ConnectOk::MainDuplicate => "MainDuplicate", ConnectOk::DagPending => "DagPending", + ConnectOk::DagConnectMissingBlock => "DagConnectMissingBlock", }; write!(f, "{}", s) } @@ -174,7 +176,7 @@ where let block_id = header.id(); let block_info = self.storage.get_block_info(block_id)?; let block_chain = if block_info.is_some() { - if self.is_main_head(&header.parent_hash(), dag_block_parents) { + if self.is_main_head(&header.parent_hash()) { None } else { let net = self.config.net(); @@ -223,13 +225,13 @@ where } #[cfg(test)] - pub fn apply_failed(&mut self, block: Block) -> Result<()> { + pub fn apply_failed(&mut self, block: Block, parents_hash: Option>) -> Result<()> { use anyhow::bail; use starcoin_chain::verifier::FullVerifier; // apply but no connection let verified_block = self.main.verify_with_verifier::(block)?; - let _executed_block = self.main.execute(verified_block)?; + let _executed_block = self.main.execute(verified_block, parents_hash)?; bail!("failed to apply for tesing the connection later!"); } @@ -241,7 +243,7 @@ where pub fn switch_new_main( &mut self, new_head_block: HashValue, - _ctx: &mut ServiceContext>, + ctx: &mut ServiceContext>, ) -> Result<()> where TransactionPoolServiceT: TxPoolSyncService, @@ -259,17 +261,9 @@ where self.update_startup_info(new_branch.head_block().header())?; let dag_parents = self.dag.lock().unwrap().get_parents(new_head_block)?; - let next_tips = self - .storage - .get_accumulator_snapshot_storage() - .get(new_head_block)? - .expect("the snapshot must exists!") - .child_hashes; - ctx.broadcast(NewHeadBlock( Arc::new(new_branch.head_block()), Some(dag_parents), - Some(next_tips), )); Ok(()) @@ -288,7 +282,6 @@ where let branch_total_difficulty = new_branch.get_total_difficulty()?; let parent_is_main_head = self.is_main_head( &executed_block.header().parent_hash(), - dag_block_parents.clone(), ); if branch_total_difficulty > main_total_difficulty { @@ -429,7 +422,7 @@ where pub fn execute( &mut self, block: Block, - dag_block_parent: Option, + dag_block_parent: Option>, ) -> Result { let chain = BlockChain::new( self.config.net().time_service(), @@ -444,24 +437,10 @@ where fn is_main_head( &self, parent_id: &HashValue, - dag_block_parents: Option>, ) -> bool { if parent_id == &self.startup_info.main { return true; } - - if let Some(block_parents) = dag_block_parents { - if self.main.status().tips_hash.is_some() && !block_parents.is_empty() { - return block_parents.into_iter().all(|block_header| { - self.main - .status() - .tips_hash - .unwrap() - .contains(&block_header) - }); - } - } - return false; } @@ -659,7 +638,7 @@ where if let Err(e) = self .bus - .broadcast(NewHeadBlock(Arc::new(block), dag_parents, next_tips)) + .broadcast(NewHeadBlock(Arc::new(block), dag_parents)) { error!("Broadcast NewHeadBlock error: {:?}", e); } @@ -720,9 +699,8 @@ where ExecutedBlock { block: block.clone(), block_info, - dag_parent: dag_block_next_parent, + parents_hash: dag_block_parents, }, - next_tips, )?; info!( "Block {} main has been processed, trigger head selection", @@ -733,7 +711,7 @@ where } (None, Some(mut branch)) => { // the block is not in the block, but the parent is - let result = branch.apply(block, dag_block_next_parent, next_tips); + let result = branch.apply(block, dag_block_parents.clone()); let executed_block = result?; self.select_head(branch, dag_block_parents)?; Ok(ConnectOk::ExeConnectBranch(executed_block)) @@ -812,7 +790,7 @@ where dag_block_next_parent: Option, next_tips: &mut Option>, ) -> Result { - let executed_block = self.main.apply(block, dag_block_next_parent, next_tips)?; + let executed_block = self.main.apply(block, dag_block_parents)?; let enacted_blocks = vec![executed_block.block().clone()]; self.do_new_head(executed_block.clone(), 1, enacted_blocks, 0, vec![])?; return Ok(ConnectOk::ExeConnectMain(executed_block)); @@ -827,7 +805,7 @@ where .dag .lock() .unwrap() - .addToDag(DagHeader::new(block.header, parents_hash))?; + .addToDag(DagHeader::new(block.header, parents_hash.clone()))?; let selected_parent = self .storage .get_block_by_hash(ghost_dag_data.selected_parent)? @@ -835,14 +813,14 @@ where let mut chain = self.main.fork(selected_parent.header.parent_hash())?; for blue_hash in ghost_dag_data.mergeset_blues.iter() { if let Some(blue_block) = self.storage.get_block(blue_hash.to_owned())? { - chain.apply(blue_block); + chain.apply(blue_block, Some(parents_hash.clone())); } else { error!("Failed to get block {:?}", blue_hash); - return Ok(DagConnectMissingBlock); + return Ok(ConnectOk::DagConnectMissingBlock); } } //self.broadcast_new_head(); - Ok(DagConnected) + Ok(ConnectOk::DagConnected) } fn connect_inner( diff --git a/sync/src/sync.rs b/sync/src/sync.rs index 59e4db783f..274e8034ec 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -168,7 +168,7 @@ impl SyncService { } let network = ctx.get_shared::()?; - let block_chain_service = ctx.service_ref::()?.clone(); + let block_chain_service = ctx.service_ref::>()?.clone(); let storage = self.storage.clone(); let self_ref = ctx.self_ref(); let connector_service = ctx diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 4200aa99ae..14bbddd69c 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -1,7 +1,6 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::block_connector::{BlockConnectedRequest, BlockConnectorService}; use crate::tasks::{BlockConnectedEventHandle, BlockFetcher, BlockLocalStore}; use crate::verified_rpc_client::RpcVerifyError; use anyhow::{format_err, Ok, Result}; @@ -16,7 +15,6 @@ use starcoin_config::G_CRATE_VERSION; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; -use starcoin_service_registry::ServiceRef; use starcoin_storage::BARNARD_HARD_FORK_HASH; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; @@ -281,10 +279,10 @@ where pub fn apply_block_for_test( &mut self, block: Block, - dag_parent: Option, + parents_hash: Option>, next_tips: &mut Option>, ) -> Result<()> { - self.apply_block(block, None, dag_parent, next_tips) + self.apply_block(block, None, parents_hash, next_tips) } fn notify_connected_block( @@ -356,7 +354,7 @@ where &mut self, block: Block, peer_id: Option, - dag_parent: Option, + parents_hash: Option>, next_tips: &mut Option>, ) -> Result<()> { if let Some((_failed_block, pre_peer_id, err, version)) = self @@ -387,9 +385,9 @@ where } let apply_result = if self.skip_pow_verify { self.chain - .apply_with_verifier::(block.clone(), dag_parent, next_tips) + .apply_with_verifier::(block.clone(), parents_hash) } else { - self.chain.apply(block.clone(), dag_parent, next_tips) + self.chain.apply(block.clone(), parents_hash) }; if let Err(err) = apply_result { let error_msg = err.to_string(); @@ -479,11 +477,11 @@ where item: SyncBlockData, next_tips: &mut Option>, ) -> Result<(Block, BlockInfo, Option>, BlockConnectAction)> { - let (block, block_info, peer_id, dag_parents, dag_transaction_header) = item.into(); + let (block, block_info, peer_id, parents_hash, dag_transaction_header) = item.into(); let block_id = block.id(); let timestamp = block.header().timestamp(); - if let Some(parents) = dag_parents.clone() { + if let Some(parents) = parents_hash.clone() { if let Some(dag) = &self.dag { // let color = dag // .lock() @@ -506,75 +504,74 @@ where ExecutedBlock { block: block.clone(), block_info: block_info.clone(), - dag_parent: dag_transaction_header, + parents_hash: parents_hash.clone(), }, - next_tips, )?; let block_info = self.chain.status().info; - Ok((block, block_info, dag_parents, BlockConnectAction::ConnectExecutedBlock)) + Ok((block, block_info, parents_hash, BlockConnectAction::ConnectExecutedBlock)) } None => { - self.apply_block(block.clone(), peer_id, dag_transaction_header, next_tips)?; + self.apply_block(block.clone(), peer_id, parents_hash.clone(), next_tips)?; self.chain.time_service().adjust(timestamp); let block_info = self.chain.status().info; - Ok((block, block_info, dag_parents, BlockConnectAction::ConnectNewBlock)) + Ok((block, block_info, parents_hash, BlockConnectAction::ConnectNewBlock)) } }; } - fn process_received_block(&self, item: SyncBlockData, next_tips: &mut Option>) -> Result { - - let s = self.collect_item(item, next_tips)?; - ///////// - // let (block, block_info, peer_id) = item.into(); - // let timestamp = block.header().timestamp(); - // let (block_info, action) = match block_info { - // Some(block_info) => { - // //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. - // //So, we just need to update chain and continue - // self.chain.connect(ExecutedBlock { - // block: block.clone(), - // block_info: block_info.clone(), - // })?; - // (block_info, BlockConnectAction::ConnectExecutedBlock) - // } - // None => { - // self.apply_block(block.clone(), peer_id)?; - // self.chain.time_service().adjust(timestamp); - // ( - // self.chain.status().info, - // BlockConnectAction::ConnectNewBlock, - // ) - // } - // }; - - //verify target - let state: Result = - if block_info.block_accumulator_info.num_leaves - == self.target.block_info.block_accumulator_info.num_leaves - { - if block_info != self.target.block_info { - Err(TaskError::BreakError( - RpcVerifyError::new_with_peers( - self.target.peers.clone(), - format!( - "Verify target error, expect target: {:?}, collect target block_info:{:?}", - self.target.block_info, - block_info - ), - ) - .into(), - ) - .into()) - } else { - Ok(CollectorState::Enough) - } - } else { - Ok(CollectorState::Need) - }; - - self.notify_connected_block(block, block_info, action, state?) - } + // fn process_received_block(&self, item: SyncBlockData, next_tips: &mut Option>) -> Result { + + // let (block, block_info, parent_hash, action) = self.collect_item(item, next_tips)?; + // ///////// + // // let (block, block_info, peer_id) = item.into(); + // // let timestamp = block.header().timestamp(); + // // let (block_info, action) = match block_info { + // // Some(block_info) => { + // // //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. + // // //So, we just need to update chain and continue + // // self.chain.connect(ExecutedBlock { + // // block: block.clone(), + // // block_info: block_info.clone(), + // // })?; + // // (block_info, BlockConnectAction::ConnectExecutedBlock) + // // } + // // None => { + // // self.apply_block(block.clone(), peer_id)?; + // // self.chain.time_service().adjust(timestamp); + // // ( + // // self.chain.status().info, + // // BlockConnectAction::ConnectNewBlock, + // // ) + // // } + // // }; + + // //verify target + // let state: Result = + // if block_info.block_accumulator_info.num_leaves + // == self.target.block_info.block_accumulator_info.num_leaves + // { + // if block_info != self.target.block_info { + // Err(TaskError::BreakError( + // RpcVerifyError::new_with_peers( + // self.target.peers.clone(), + // format!( + // "Verify target error, expect target: {:?}, collect target block_info:{:?}", + // self.target.block_info, + // block_info + // ), + // ) + // .into(), + // ) + // .into()) + // } else { + // Ok(CollectorState::Enough) + // } + // } else { + // Ok(CollectorState::Need) + // }; + + // self.notify_connected_block(block, block_info, action, state?, parent_hash) + // } } impl TaskResultCollector for BlockCollector @@ -613,9 +610,9 @@ where let mut next_tips = Some(vec![]); let mut block_to_broadcast = vec![]; - process_block_pool.into_iter().try_fold((), |_, item| { - block_to_broadcast.push(self.collect_item(item, &mut next_tips)) - }); + for item in process_block_pool { + block_to_broadcast.push(self.collect_item(item, &mut next_tips)?) + } //verify target match self.target { diff --git a/sync/src/tasks/inner_sync_task.rs b/sync/src/tasks/inner_sync_task.rs index 43b53c16e2..e016ef1498 100644 --- a/sync/src/tasks/inner_sync_task.rs +++ b/sync/src/tasks/inner_sync_task.rs @@ -15,6 +15,7 @@ use starcoin_service_registry::ServiceRef; use starcoin_storage::Store; use starcoin_sync_api::SyncTarget; use starcoin_time_service::TimeService; +use starcoin_txpool::TxPoolService; use starcoin_types::block::{BlockIdAndNumber, BlockInfo}; use std::cmp::min; use std::sync::Arc; @@ -86,7 +87,7 @@ where max_retry_times: u64, delay_milliseconds_on_error: u64, skip_pow_verify_when_sync: bool, - block_chain_service: ServiceRef, + block_chain_service: ServiceRef>, vm_metrics: Option, ) -> Result<(BlockChain, TaskHandle), TaskError> { let buffer_size = self.target.peers.len(); diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 90eb1422cf..f1f71f48c3 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -626,7 +626,7 @@ pub fn full_sync_task( ancestor_event_handle: A, peer_provider: N, max_retry_times: u64, - block_chain_service: ServiceRef, + block_chain_service: ServiceRef>, sync_metrics: Option, vm_metrics: Option, ) -> Result<( diff --git a/sync/src/tasks/sync_dag_full_task.rs b/sync/src/tasks/sync_dag_full_task.rs index fd21affaad..d327d84c9a 100644 --- a/sync/src/tasks/sync_dag_full_task.rs +++ b/sync/src/tasks/sync_dag_full_task.rs @@ -1,14 +1,13 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, format_err, Ok}; -use async_std::task::Task; use futures::{future::BoxFuture, FutureExt}; use network_api::PeerProvider; use starcoin_accumulator::{ accumulator_info::AccumulatorInfo, Accumulator, AccumulatorTreeStore, MerkleAccumulator, }; use starcoin_chain::BlockChain; -use starcoin_chain_api::{ChainReader, ChainWriter}; +use starcoin_chain_api::ChainReader; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; @@ -17,6 +16,7 @@ use starcoin_network::NetworkServiceRef; use starcoin_service_registry::ServiceRef; use starcoin_storage::{flexi_dag::SyncFlexiDagSnapshotStorage, storage::CodecKVStore, Store}; use starcoin_time_service::TimeService; +use starcoin_txpool::TxPoolService; use stream_task::{ Generator, TaskError, TaskEventCounterHandle, TaskFuture, TaskGenerator, TaskHandle, }; @@ -180,7 +180,7 @@ async fn sync_dag_block( network: N, skip_pow_verify_when_sync: bool, dag: Arc>, - block_chain_service: ServiceRef, + block_chain_service: ServiceRef>, vm_metrics: Option, ) -> anyhow::Result where @@ -279,11 +279,11 @@ pub fn sync_dag_full_task( local_store: Arc, time_service: Arc, vm_metrics: Option, - connector_service: ServiceRef, + connector_service: ServiceRef>, network: NetworkServiceRef, skip_pow_verify_when_sync: bool, dag: Arc>, - block_chain_service: ServiceRef, + block_chain_service: ServiceRef>, ) -> anyhow::Result<( BoxFuture<'static, anyhow::Result>, TaskHandle, diff --git a/vm/compiler/Cargo.toml b/vm/compiler/Cargo.toml index 7eb7dde473..4665969ede 100644 --- a/vm/compiler/Cargo.toml +++ b/vm/compiler/Cargo.toml @@ -24,7 +24,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-move-compiler" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/dev/Cargo.toml b/vm/dev/Cargo.toml index 33fbf4bf84..a2a93e9180 100644 --- a/vm/dev/Cargo.toml +++ b/vm/dev/Cargo.toml @@ -21,7 +21,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-dev" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/gas-algebra-ext/Cargo.toml b/vm/gas-algebra-ext/Cargo.toml index b702bf116b..ef7dee9696 100644 --- a/vm/gas-algebra-ext/Cargo.toml +++ b/vm/gas-algebra-ext/Cargo.toml @@ -16,7 +16,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-gas-algebra-ext" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/move-coverage/Cargo.toml b/vm/move-coverage/Cargo.toml index 54d25b4713..8b320b8bce 100644 --- a/vm/move-coverage/Cargo.toml +++ b/vm/move-coverage/Cargo.toml @@ -21,7 +21,7 @@ edition = { workspace = true } license = { workspace = true } name = "move-coverage" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/move-explain/Cargo.toml b/vm/move-explain/Cargo.toml index 3539fd50c4..7e5d9857b4 100644 --- a/vm/move-explain/Cargo.toml +++ b/vm/move-explain/Cargo.toml @@ -16,5 +16,5 @@ license = { workspace = true } name = "starcoin-move-explain" publish = { workspace = true } repository = { workspace = true } -version = "1.13.5" +version = "1.13.7" rust-version = { workspace = true } diff --git a/vm/move-package-manager/Cargo.toml b/vm/move-package-manager/Cargo.toml index fa7606377b..5129c3bfbd 100644 --- a/vm/move-package-manager/Cargo.toml +++ b/vm/move-package-manager/Cargo.toml @@ -67,5 +67,5 @@ license = { workspace = true } name = "move-package-manager" publish = { workspace = true } repository = { workspace = true } -version = "1.13.5" +version = "1.13.7" rust-version = { workspace = true } diff --git a/vm/move-package-manager/src/release.rs b/vm/move-package-manager/src/release.rs index 0b13e227a5..a1f47792d7 100644 --- a/vm/move-package-manager/src/release.rs +++ b/vm/move-package-manager/src/release.rs @@ -23,7 +23,7 @@ pub const DEFAULT_RELEASE_DIR: &str = "release"; pub struct Release { #[clap(name = "move-version", long = "move-version", default_value="6", possible_values=&["5", "6"])] /// specify the move lang version for the release. - /// currently, only v5, v6 are supported. + /// currently, only v6 are supported. language_version: u8, #[clap(name="release-dir", long, parse(from_os_str), default_value=DEFAULT_RELEASE_DIR)] @@ -78,7 +78,6 @@ pub fn handle_release( for m in pkg.root_compiled_units.as_slice() { let m = module(&m.unit)?; println!("\t {}", m.self_id()); - // XXX FIXME YSG, mpm release let code = if language_version as u32 == VERSION_4 { ModuleBytecodeDowngrader::to_v4(m)? } else { diff --git a/vm/move-prover/Cargo.toml b/vm/move-prover/Cargo.toml index 18bb9d37f7..55d63315bd 100644 --- a/vm/move-prover/Cargo.toml +++ b/vm/move-prover/Cargo.toml @@ -35,7 +35,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-move-prover" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/natives/Cargo.toml b/vm/natives/Cargo.toml index 3d0696061d..9b05d70c47 100644 --- a/vm/natives/Cargo.toml +++ b/vm/natives/Cargo.toml @@ -30,7 +30,7 @@ testing = ["move-stdlib/testing"] authors = { workspace = true } edition = { workspace = true } name = "starcoin-natives" -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } license = { workspace = true } publish = { workspace = true } diff --git a/vm/resource-viewer/Cargo.toml b/vm/resource-viewer/Cargo.toml index 848a0e3755..46f1c9c848 100644 --- a/vm/resource-viewer/Cargo.toml +++ b/vm/resource-viewer/Cargo.toml @@ -13,7 +13,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-resource-viewer" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-gas/Cargo.toml b/vm/starcoin-gas/Cargo.toml index c1e7983495..3ebace8295 100644 --- a/vm/starcoin-gas/Cargo.toml +++ b/vm/starcoin-gas/Cargo.toml @@ -16,7 +16,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-gas" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-transactional-test-harness/Cargo.toml b/vm/starcoin-transactional-test-harness/Cargo.toml index 8c0c9f1868..a93426a4a0 100644 --- a/vm/starcoin-transactional-test-harness/Cargo.toml +++ b/vm/starcoin-transactional-test-harness/Cargo.toml @@ -70,7 +70,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-transactional-test-harness" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/starcoin-transactional-test-harness/src/fork_chain.rs b/vm/starcoin-transactional-test-harness/src/fork_chain.rs index 7431565fa8..2ad3fb7311 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_chain.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_chain.rs @@ -114,11 +114,7 @@ impl ForkBlockChain { }) } - pub fn add_new_block( - &mut self, - mut block: Block, - dag_parent: Option>, - ) -> Result<()> { + pub fn add_new_block(&mut self, mut block: Block) -> Result<()> { block.header = block.header().as_builder().build(); let block_accumulator = MerkleAccumulator::new_empty( @@ -136,7 +132,7 @@ impl ForkBlockChain { self.number_hash_map .insert(self.current_number, block.header().id()); self.status = Some(ChainStatusWithBlock { - status: ChainStatus::new(block.header().clone(), block_info.clone(), dag_parent), + status: ChainStatus::new(block.header().clone(), block_info.clone(), None), head: block.clone(), }); self.storage.save_block_info(block_info)?; @@ -147,7 +143,7 @@ impl ForkBlockChain { pub fn add_new_txn(&mut self, txn: Transaction, output: TransactionOutput) -> Result<()> { let txn_hash = txn.id(); let state_root = *self.state_root.lock().unwrap(); - let (_, events, gas_used, status) = output.into_inner(); + let (_, _, events, gas_used, status) = output.into_inner(); let status = status .status() .expect("TransactionStatus at here must been KeptVMStatus"); diff --git a/vm/starcoin-transactional-test-harness/src/fork_state.rs b/vm/starcoin-transactional-test-harness/src/fork_state.rs index 6266115f3b..dec9571d5d 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_state.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_state.rs @@ -21,7 +21,7 @@ use starcoin_types::access_path::AccessPath; use starcoin_types::account_state::AccountState; use starcoin_types::state_set::AccountStateSet; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use tokio::runtime::Runtime; pub struct MockStateNodeStore { @@ -72,6 +72,10 @@ impl StateNodeStore for MockStateNodeStore { let batch = CodecWriteBatch::new_puts(nodes.into_iter().collect()); self.local_storage.write_batch(batch) } + + fn get_table_info(&self, _address: AccountAddress) -> Result> { + Ok(None) + } } #[derive(Clone)] @@ -158,4 +162,9 @@ impl ChainStateAsyncService for MockChainStateAsyncService { let reader = self.state_db().fork_at(state_root); reader.get_with_table_item_proof(&handle, &key) } + + async fn get_table_info(self, address: AccountAddress) -> Result> { + let reader = self.state_db().fork(); + reader.get_table_info(address) + } } diff --git a/vm/starcoin-transactional-test-harness/src/lib.rs b/vm/starcoin-transactional-test-harness/src/lib.rs index 5cf3112902..6e023aabfe 100644 --- a/vm/starcoin-transactional-test-harness/src/lib.rs +++ b/vm/starcoin-transactional-test-harness/src/lib.rs @@ -646,7 +646,7 @@ impl<'a> StarcoinTestAdapter<'a> { TransactionStatus::Keep(kept_vm_status) => match kept_vm_status { KeptVMStatus::Executed => { self.context - .apply_write_set(output.clone().into_inner().0)?; + .apply_write_set(output.clone().into_inner().1)?; } _ => { bail!("Failed to execute transaction. VMStatus: {}", status) @@ -655,6 +655,9 @@ impl<'a> StarcoinTestAdapter<'a> { TransactionStatus::Discard(_) => { bail!("Transaction discarded. VMStatus: {}", status) } + TransactionStatus::Retry => { + bail!("Transaction Retry never happen") + } } let mut chain = self.context.chain.lock().unwrap(); chain.add_new_txn(Transaction::BlockMetadata(meta), output)?; @@ -681,7 +684,7 @@ impl<'a> StarcoinTestAdapter<'a> { match output.status() { TransactionStatus::Keep(_kept_vm_status) => { self.context - .apply_write_set(output.clone().into_inner().0)?; + .apply_write_set(output.clone().into_inner().1)?; let mut chain = self.context.chain.lock().unwrap(); chain.add_new_txn( Transaction::UserTransaction(signed_txn.clone()), @@ -689,6 +692,7 @@ impl<'a> StarcoinTestAdapter<'a> { )?; } TransactionStatus::Discard(_) => {} + TransactionStatus::Retry => {} } let payload = decode_txn_payload(&self.context.storage, signed_txn.payload())?; let mut txn_view: SignedUserTransactionView = signed_txn.try_into()?; @@ -869,7 +873,7 @@ impl<'a> StarcoinTestAdapter<'a> { ); let new_block = Block::new(block_header, block_body); let mut chain = self.context.chain.lock().unwrap(); - chain.add_new_block(new_block, None)?; + chain.add_new_block(new_block)?; Ok((None, Some(serde_json::to_value(&new_block_meta)?))) } diff --git a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp index 909fa3b096..6d1cfb7a18 100644 --- a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp +++ b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd.exp @@ -2,7 +2,7 @@ processed 10 tasks task 5 'run'. lines 11-19: { - "gas_used": 15127, + "gas_used": 11125, "status": "Executed" } diff --git a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp index aaf0bb0de7..878d67d4d6 100644 --- a/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp +++ b/vm/starcoin-transactional-test-harness/tests/cases/call_api_cmd_halley.exp @@ -2,6 +2,6 @@ processed 4 tasks task 3 'run'. lines 7-14: { - "gas_used": 10461, + "gas_used": 8712, "status": "Executed" } diff --git a/vm/stdlib/Cargo.toml b/vm/stdlib/Cargo.toml index fe6032b018..745e5af3c3 100644 --- a/vm/stdlib/Cargo.toml +++ b/vm/stdlib/Cargo.toml @@ -29,7 +29,7 @@ edition = { workspace = true } license = { workspace = true } name = "stdlib" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/stdlib/compiled/12/11-12/stdlib.blob b/vm/stdlib/compiled/12/11-12/stdlib.blob index 65e03ac824b9d69953234f6670cb1ad5c4d662e0..e17900e644114cff80e894966b33de13ad05eaf3 100644 GIT binary patch delta 23870 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 N1;)}|S-^E0{|B|gUu*yX delta 21897 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 delta 394 zcmYk2u}T9$5Qb-FcklLg<}fiAPcfB|gn*>6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFskE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ diff --git a/vm/stdlib/compiled/12/11-12/stdlib/013_Option.mv b/vm/stdlib/compiled/12/11-12/stdlib/013_Option.mv index 9a0dae2f25d1cc22a2f2e2ccde39c9a13bfcb2f2..340a58f50f415907a26ff143f03e2cab343beaba 100644 GIT binary patch delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H delta 575 zcmYjPJ8l#~5Uo#7cTM;7?9S}$tc@HX5;6h;+_r?|hyV_;;$sO3SzciiIN}5Zs}T_r zk`I6gi3kwffW$X&V9D$aaP0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD diff --git a/vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv b/vm/stdlib/compiled/12/11-12/stdlib/014_BCS.mv index d58d5363cc266a5cbb3bec62584544ee56c164b7..d66fd2976727d10389ab808b938c050b7740b0c9 100644 GIT binary patch delta 656 zcmY*XJ!@1!6g_j_e7raFKK5<0pV?h8F}sP1ZazR8Wesa5SXl-MxJAGuN}@#?!G9o= zHbp=JmJ;j)@gG=NiA5lVg^k(^-nSbOZ*jQ8IrrRqXXZowW&Oob>$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U05(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy diff --git a/vm/stdlib/compiled/12/11-12/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/12/11-12/stdlib/036_Authenticator.mv index 7fff119e122a9a7d9b41cf70cc56ef926141b43c..c5a74c50726f9630bc63c08e50f2b5271cbbcc8f 100644 GIT binary patch delta 396 zcmYk1u}T9$5Qb-FcW3Wr_u`!#f+h$Fh*($z1kcU{!9v8&RsxCyG=-?0jc*XsS(!Ib zu<|8*42k*zcFv{IVT<|qpZWIR8Be)8ZG8m>fPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN delta 469 zcmW+yO-h_W5Ur}NuAct;=KIV?AZ9iy5+Z~!kVUUu3JG|EVT^+sbw(y*M9^gxSx)8x z@d}cq8;{@ta)6Laq&{oWyw~;mb-lWizcRcV{EGqr5g|}uQ|B3IVFBaEwFq_Y3SN`Ji>+$Gd{P}csg}0vyBnmi$P43y~Y~$Vp{19lH8{0Zk8p**n zzp?{oI;P85140A{n-vsvu)$)zV{H~%?a{`UV%kI*BNdKDRQX&lld(Bs6G;<;mkbV7 z08P?}0R6WCYDhL%oyZhQ*lqr~bU+GbiwCwJGS>9V1`i+&i#!@H z?HPHQ#d{rmnakJ_cKG0fLkIh!UtnJyJ~$k{kM53e_~Q49Wh5)?3i`{;m+!rNTfXG; z=*v>=i@h(kDTMGrv|{U3W9?5;zEDrpbMp^|^`-M+-hSBCcIo}P z{qJ>|XQfvEixOskRYCy*g(0L+!W5RUg(F;{g(ot?7g-UIez1iI>6f#Cxa)|Lr&YnM z2~jVFXdpr(jeBTpqK#&n?=^*&k;Lv*NvC6h5Nb(+365(C(INn(Xic#V4IzpsiO{YX z_aW)&Fp#F5$b4NEW4F!Nlj45I6`~jEe9?yl{hHta%MCCLddzw-!+6MNjIxYF0huuz z5*&7TUCPmPq>iUYQjA%8)Cq(bbMitQMU%G1Q#@9nt>e5u!EmC!L5SK(Rxs%hoN8n( zQ+pVvn;1`1sKgm36yj`p*tyi(^K8O}Ji*0gf*FQOdkJQ-MTpA3hg;o@3q6dt_cPwyb$y@QPR z4-tA_AEJoM_gh+o^OTY!-47fT_>GbQ!vE$G0AYU1Fa& zhtS94M?jd zgY#0zA2OTqBW9C7PUk-XK;i!sF4CcypQWeCpYskRU{LMeT<$OMRD{70vXe`P_d!j4RTy!;>KRSPWeq#Q_^5GPj^?NPquasl{IST0?u|@Flf(CiC!z;YQv_b}(D>x6 zauH1rkp7gR48xETr8G^C=Z3|ji!^g~XWP3>wReb8CZTKqm2}JTWL=IXznA-(rIe&i zDzI$IlJ>z7V7iVVV)75!RF}KDR#{!I+}~Jwv^rB+tvsx*CI6CRQqEL1o)4)PMq)a{ zh5XWbd2w@Py1w4H@zwd|2i0nMeQ9NLxw27Bj_ARG?8f`6 z~+e4qocy@!m}J30hA z^EF2KE~A(=5Mi0>2UgOUN;Fm_$v$#Xq4Ah`?DrVWHAdG&SXUNCi*N07{E~ z2vi&(H9P~*V2TE-J`OQQ{Jb0?LY%|8Ed~r+GSfpB5Tz(EctV;v38yv z=u=sXR6x#-_u?Kh2EfsRBy3XHq%kMs7HoQj?rhUDaVv7q1Vf8nK-Ap${UgXeTBmymSi=*x6+S2TdD6O6EylpK0ZKI1)3_0%4Gk4Zw|b& z*~?x5_HCt>4BRT6aNlllbK$k*Wxi)-kX&%EpWc zn(d1~=Q(fW#UUKrC1#jN1sg|6xzO2pra<9lMh|^>EtpybbR{{M$!`jsZ3dZm#^zjj zRKyyyqJSmRif0RKaPpTzM{R`6pno=eig;MUBX5ZK1&rBOvMDg~I7;(#oqUxvHZ+zz z%e6{W$}+s;*{(g5CiN9)m-Vjo2DQ^us?qV3RgcFJX5u4+cUN4+1k=JjtROy zC_DZFg z=Kql0k)x4BkENm)E|W!NK2V!%d^qZPo^~M~cD-&Hs5;9I-|mTJQMQGZ*l}yRTkchD zmdEu^ch+-X?Ru`8^$V^$7#3yZ8}Z?y=cr-FbF?FmY9pM{E~Qi*BmJ&^Gs<#S!?@61 zT=Ftug=U$!o<@pLJQ5$l<7{xvAkj2n|2Ispx*Fp|>gvohZggepD9v<>9ZQFc8?x7qFoNCp`8tm;O=6WIp*^z;?zKgwbLzcs=GU)oP^~XXF=QD@}XMZ*>I8U z@$=nsUA@n=fDENqfNAZrral5IOB6h@RVzF1$^f3^dqQ0?$lxSTcb=G0JVndZ3`iCAWB%eXx)D*i70HKSy`}#mf_>TifsvuBULJB2p;RsiF!WUWuA}2z&>mU*rNw#>6SmTLV24DMYnM zL?aPR^lYYs7NT2ip|vD&{hA{j#{nVKsstNc*Ab#k07y})VLMtvbf6?gr(!H4+3B*7 z$bHCi-40{LW$dY8ujdQV$ARxhQ5B+J6PdE!0K;IwqC+{x;gE48&v+oBFh*m7F^}I5 z7Kl95z~p!hCs=#Zi-a&Z5#G*$QPSl*8tYv$OLpYrfoM|IC%kWZ(;2gFK@p6&il@5A6-%0SQrWBWIHMme_ zn-{wnFYRNz++88O($hrwU+rc3S|8)`e#YzlgnDa0f|U2$+lBiRs=f5KJruYzY=Ow_ z9sm&b1IrS*w_ubQ566ZH-#$oed}N{U-yzER=^-109vml^`rQiSd%&h|zywJ3z5%G( zvPvGD58!Z0q8|cK>vA8#d^-sFGoT~&F;I=1pF?^Cg!%<|N5OV}3CGF4{DcLpPgy{I z#&qs6%#+LDb8eqv-%o*Fql za%RLCJQtlylJw=YoW|*7T1Z>cSJLz8tLf6@()7~I((J`(Y3^cjX|OZBJaR3$7A?;% z&n!nhcpM0SHYz2)Q>$@u(>znI4cXqN-Z|=#*-d_NU*_JM5o4S+_ zckXO#u5Yib-nzBDzO$2kqla_B?%SIy_qVt1ZEZYQ&$@!e?6*Pw$rr(|p-HX3d4Knj zy|%Tzz4g$$|6u3#%G&D9yIDtgshHc{y1Tyl%#wX?eRE^?cD57FWlzIHPreUVp?bs3 zQ^@T;zOEy1gB3>S4IP7w0cf&`8)@_z??=adwX7p`@ zyUJm35$~#dK6(gmD(cEzr4cqEU@C|Dxfv_XzKuSCW7$sp$R3*1B3{V8i~Ek83~ai~ zdw&6%Frd~tqFWBAat=b1R$nFgdiV*y&s(cmks%|ou4yZ*jA;&fTcx0 zAT2Sv`BBX-kf(fEx=K4rHqhy_gpnVy1&v=;$wO%{~2-RxSDOUlgRC`>PQafxj$vd@#AIn!sC>y7J@w$kj! zkFWIA+#}&t`pQwu3xU~BnL4JQikZ@HfPRD7zmt|K)yfQ$<&lCKQrN@R3#{I+$};cM zSJJ#7p`%qMa|bC44+p3o78fC2m=n{&wBaD_D+{bpm_sw58(u7K%OEi!AU`Bpoo1jBg&FzU)2VsA-=%_kPCnu0UFMhHtb#rO-M5;wK9`9 zRXaGbK(epVus}eAIx7N?kG0*@JRbPIR~G$&I!+F7x)#*Q_xFN| z+p08fo}=T{J{ofBQ>bDmrU|bF?D!75?O`|Znh#yFQ&qGBPL5_%jWOGIB9U~-A$qIx zeL3!{@n)ykBWKhR<_09qNjC?!|Gza>ksC(J>REN3WfaSRnbG!mue_peJk#@W+$na; zHMPNLAj;ukz8v-KcHPY4 z+>5fKIA3F&rfU=B5^5RUSR#D7A?7fzATx#Z{{eOVOqpkww-ZT8-{LjF<3`^-S`*oq Wt!+_>wtHQZu92C_zG*FED*g*={3fsf diff --git a/vm/stdlib/compiled/12/11-12/stdlib/040_Ring.mv b/vm/stdlib/compiled/12/11-12/stdlib/040_Ring.mv index 868c648d3443124b11a5ee0c27a78862b3894682..870e433ee9fc90ba055fbebda1186a8e2267f49c 100644 GIT binary patch delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(d`f39kAnZetKPj@Jqn85@T%w4tJmFA{oFotC$C4}U80mKKxLS*u1~?d zfi?OHA0R!|PceH%o#ZW9`;NT(M981%rzzgs#DBE05ELq{lmVp>Q$b}ENFdaLi6Oz1 z62UBq={(B?t+9g6oiHdUiIg^-3Y4Z^E!gQxm99$BW49EE?hQ-9deIBh2dTvY$MujS zzmN)|0dc@ZFM3>RHOr&Ok491HbGGc5x$vIZm)!A{_g$Giu6>^h8u+1K@ckEGc5Yx| zbEP4PkU9W~n6^}!D~L(7|IoRjKcBi~B@a=KS~s^~J=q@b&ZmdX6L@?+*&k2t&-ZA2 zFqG4i0Z7$;#%fELiv~Lb@`(gmr zbj^Y@t7d~XoouSA?1EEU;24&^Ya9ymP=CH_}RayQY0PU~1^HeOL}VJ!}M`|6h0 st=a_Pg_9dvy&fu4Iq65}29n*ySk}EzvzW7q%Buc*zUUZq^UbXN0!}kdLI3~& diff --git a/vm/stdlib/compiled/12/11-12/stdlib/043_BlockReward.mv b/vm/stdlib/compiled/12/11-12/stdlib/043_BlockReward.mv index 533de2359e99cadbee5359e8418a3d24c7ac3322..f2b272971c95f54f03e0c167c1b19ab91bce5520 100644 GIT binary patch delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ delta 547 zcmXYt&uddb5XX1+=i8m#_wwFLn^S>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk diff --git a/vm/stdlib/compiled/12/11-12/stdlib/044_Collection.mv b/vm/stdlib/compiled/12/11-12/stdlib/044_Collection.mv index 14cb6885c15c02e39d50fe39f2f4d45ea04ee280..306c01fc8c236d304380ad8666cf6810adb50368 100644 GIT binary patch delta 352 zcmW-cJx&8b42AtXzZuVNGP?nq2#G2MeKI770tpHvZh+t#bd*SRX(^Dn01cNwoB%F> z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~NETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfmcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= diff --git a/vm/stdlib/compiled/12/11-12/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/12/11-12/stdlib/051_EVMAddress.mv index 486a2ad6212e59a89d0807ca7c0d0c46a3600c74..9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7 100644 GIT binary patch delta 173 zcmYL>I|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS`3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;FPMa@gAH2|XM+wx&KRM1N{RFhPb&7^wpUtJ?040;u7IIoUAORiSrY_{F?$ny)5}OUZ58 zR9_|wOUqj9pg)nGn!1**H?c0xZ6%)L%GXDBb7wg&3Rnt9sn}rWX>L#5aWcoYjY~bX h>0agCnKH4b9ulmnEhhm+r7SL_%C5Lf2MWJ01b;C>E3*Iq delta 394 zcmYk2u}T9$5Qb-FcklLg<}fiAPcfB|gn*>6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFskE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ diff --git a/vm/stdlib/compiled/12/stdlib/013_Option.mv b/vm/stdlib/compiled/12/stdlib/013_Option.mv index 9a0dae2f25d1cc22a2f2e2ccde39c9a13bfcb2f2..340a58f50f415907a26ff143f03e2cab343beaba 100644 GIT binary patch delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H delta 575 zcmYjPJ8l#~5Uo#7cTM;7?9S}$tc@HX5;6h;+_r?|hyV_;;$sO3SzciiIN}5Zs}T_r zk`I6gi3kwffW$X&V9D$aaP0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD diff --git a/vm/stdlib/compiled/12/stdlib/014_BCS.mv b/vm/stdlib/compiled/12/stdlib/014_BCS.mv index d58d5363cc266a5cbb3bec62584544ee56c164b7..d66fd2976727d10389ab808b938c050b7740b0c9 100644 GIT binary patch delta 656 zcmY*XJ!@1!6g_j_e7raFKK5<0pV?h8F}sP1ZazR8Wesa5SXl-MxJAGuN}@#?!G9o= zHbp=JmJ;j)@gG=NiA5lVg^k(^-nSbOZ*jQ8IrrRqXXZowW&Oob>$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U05(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy diff --git a/vm/stdlib/compiled/12/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/12/stdlib/036_Authenticator.mv index 7fff119e122a9a7d9b41cf70cc56ef926141b43c..c5a74c50726f9630bc63c08e50f2b5271cbbcc8f 100644 GIT binary patch delta 396 zcmYk1u}T9$5Qb-FcW3Wr_u`!#f+h$Fh*($z1kcU{!9v8&RsxCyG=-?0jc*XsS(!Ib zu<|8*42k*zcFv{IVT<|qpZWIR8Be)8ZG8m>fPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN delta 469 zcmW+yO-h_W5Ur}NuAct;=KIV?AZ9iy5+Z~!kVUUu3JG|EVT^+sbw(y*M9^gxSx)8x z@d}cq8;{@ta)6Laq&{oWyw~;mb-lWizcRcV{EGqr5g|}uQ|B3IVFBaEwFq_Y3SN`Ji>+$Gd{P}csg}0vyBnmi$P43y~Y~$Vp{19lH8{0Zk8p**n zzp?{oI;P85140A{n-vsvu)$)zV{H~%?a{`UV%kI*BNdKDRQX&lld(Bs6G;<;mkbV7 z08P?}0R6WCYDhL%oyZhQ*lqr~bU+GbiwCwJGS>9V1`i+&i#!@H z?HPHQ#d{rmnakJ_cKG0fLkIh!UtnJyJ~$k{kM53e_~Q49Wh5)?3i`{;m+!rNTfXG; z=*v>=i@h(kDTMGrv|{U3W9?5;zEDrpbMp^|^`-M+-hSBCcIo}P z{qJ>|XQfvEixOskRYCy*g(0L+!W5RUg(F;{g(ot?7g-UIez1iI>6f#Cxa)|Lr&YnM z2~jVFXdpr(jeBTpqK#&n?=^*&k;Lv*NvC6h5Nb(+365(C(INn(Xic#V4IzpsiO{YX z_aW)&Fp#F5$b4NEW4F!Nlj45I6`~jEe9?yl{hHta%MCCLddzw-!+6MNjIxYF0huuz z5*&7TUCPmPq>iUYQjA%8)Cq(bbMitQMU%G1Q#@9nt>e5u!EmC!L5SK(Rxs%hoN8n( zQ+pVvn;1`1sKgm36yj`p*tyi(^K8O}Ji*0gf*FQOdkJQ-MTpA3hg;o@3q6dt_cPwyb$y@QPR z4-tA_AEJoM_gh+o^OTY!-47fT_>GbQ!vE$G0AYU1Fa& zhtS94M?jd zgY#0zA2OTqBW9C7PUk-XK;i!sF4CcypQWeCpYskRU{LMeT<$OMRD{70vXe`P_d!j4RTy!;>KRSPWeq#Q_^5GPj^?NPquasl{IST0?u|@Flf(CiC!z;YQv_b}(D>x6 zauH1rkp7gR48xETr8G^C=Z3|ji!^g~XWP3>wReb8CZTKqm2}JTWL=IXznA-(rIe&i zDzI$IlJ>z7V7iVVV)75!RF}KDR#{!I+}~Jwv^rB+tvsx*CI6CRQqEL1o)4)PMq)a{ zh5XWbd2w@Py1w4H@zwd|2i0nMeQ9NLxw27Bj_ARG?8f`6 z~+e4qocy@!m}J30hA z^EF2KE~A(=5Mi0>2UgOUN;Fm_$v$#Xq4Ah`?DrVWHAdG&SXUNCi*N07{E~ z2vi&(H9P~*V2TE-J`OQQ{Jb0?LY%|8Ed~r+GSfpB5Tz(EctV;v38yv z=u=sXR6x#-_u?Kh2EfsRBy3XHq%kMs7HoQj?rhUDaVv7q1Vf8nK-Ap${UgXeTBmymSi=*x6+S2TdD6O6EylpK0ZKI1)3_0%4Gk4Zw|b& z*~?x5_HCt>4BRT6aNlllbK$k*Wxi)-kX&%EpWc zn(d1~=Q(fW#UUKrC1#jN1sg|6xzO2pra<9lMh|^>EtpybbR{{M$!`jsZ3dZm#^zjj zRKyyyqJSmRif0RKaPpTzM{R`6pno=eig;MUBX5ZK1&rBOvMDg~I7;(#oqUxvHZ+zz z%e6{W$}+s;*{(g5CiN9)m-Vjo2DQ^us?qV3RgcFJX5u4+cUN4+1k=JjtROy zC_DZFg z=Kql0k)x4BkENm)E|W!NK2V!%d^qZPo^~M~cD-&Hs5;9I-|mTJQMQGZ*l}yRTkchD zmdEu^ch+-X?Ru`8^$V^$7#3yZ8}Z?y=cr-FbF?FmY9pM{E~Qi*BmJ&^Gs<#S!?@61 zT=Ftug=U$!o<@pLJQ5$l<7{xvAkj2n|2Ispx*Fp|>gvohZggepD9v<>9ZQFc8?x7qFoNCp`8tm;O=6WIp*^z;?zKgwbLzcs=GU)oP^~XXF=QD@}XMZ*>I8U z@$=nsUA@n=fDENqfNAZrral5IOB6h@RVzF1$^f3^dqQ0?$lxSTcb=G0JVndZ3`iCAWB%eXx)D*i70HKSy`}#mf_>TifsvuBULJB2p;RsiF!WUWuA}2z&>mU*rNw#>6SmTLV24DMYnM zL?aPR^lYYs7NT2ip|vD&{hA{j#{nVKsstNc*Ab#k07y})VLMtvbf6?gr(!H4+3B*7 z$bHCi-40{LW$dY8ujdQV$ARxhQ5B+J6PdE!0K;IwqC+{x;gE48&v+oBFh*m7F^}I5 z7Kl95z~p!hCs=#Zi-a&Z5#G*$QPSl*8tYv$OLpYrfoM|IC%kWZ(;2gFK@p6&il@5A6-%0SQrWBWIHMme_ zn-{wnFYRNz++88O($hrwU+rc3S|8)`e#YzlgnDa0f|U2$+lBiRs=f5KJruYzY=Ow_ z9sm&b1IrS*w_ubQ566ZH-#$oed}N{U-yzER=^-109vml^`rQiSd%&h|zywJ3z5%G( zvPvGD58!Z0q8|cK>vA8#d^-sFGoT~&F;I=1pF?^Cg!%<|N5OV}3CGF4{DcLpPgy{I z#&qs6%#+LDb8eqv-%o*Fql za%RLCJQtlylJw=YoW|*7T1Z>cSJLz8tLf6@()7~I((J`(Y3^cjX|OZBJaR3$7A?;% z&n!nhcpM0SHYz2)Q>$@u(>znI4cXqN-Z|=#*-d_NU*_JM5o4S+_ zckXO#u5Yib-nzBDzO$2kqla_B?%SIy_qVt1ZEZYQ&$@!e?6*Pw$rr(|p-HX3d4Knj zy|%Tzz4g$$|6u3#%G&D9yIDtgshHc{y1Tyl%#wX?eRE^?cD57FWlzIHPreUVp?bs3 zQ^@T;zOEy1gB3>S4IP7w0cf&`8)@_z??=adwX7p`@ zyUJm35$~#dK6(gmD(cEzr4cqEU@C|Dxfv_XzKuSCW7$sp$R3*1B3{V8i~Ek83~ai~ zdw&6%Frd~tqFWBAat=b1R$nFgdiV*y&s(cmks%|ou4yZ*jA;&fTcx0 zAT2Sv`BBX-kf(fEx=K4rHqhy_gpnVy1&v=;$wO%{~2-RxSDOUlgRC`>PQafxj$vd@#AIn!sC>y7J@w$kj! zkFWIA+#}&t`pQwu3xU~BnL4JQikZ@HfPRD7zmt|K)yfQ$<&lCKQrN@R3#{I+$};cM zSJJ#7p`%qMa|bC44+p3o78fC2m=n{&wBaD_D+{bpm_sw58(u7K%OEi!AU`Bpoo1jBg&FzU)2VsA-=%_kPCnu0UFMhHtb#rO-M5;wK9`9 zRXaGbK(epVus}eAIx7N?kG0*@JRbPIR~G$&I!+F7x)#*Q_xFN| z+p08fo}=T{J{ofBQ>bDmrU|bF?D!75?O`|Znh#yFQ&qGBPL5_%jWOGIB9U~-A$qIx zeL3!{@n)ykBWKhR<_09qNjC?!|Gza>ksC(J>REN3WfaSRnbG!mue_peJk#@W+$na; zHMPNLAj;ukz8v-KcHPY4 z+>5fKIA3F&rfU=B5^5RUSR#D7A?7fzATx#Z{{eOVOqpkww-ZT8-{LjF<3`^-S`*oq Wt!+_>wtHQZu92C_zG*FED*g*={3fsf diff --git a/vm/stdlib/compiled/12/stdlib/040_Ring.mv b/vm/stdlib/compiled/12/stdlib/040_Ring.mv index 868c648d3443124b11a5ee0c27a78862b3894682..870e433ee9fc90ba055fbebda1186a8e2267f49c 100644 GIT binary patch delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(d`f39kAnZetKPj@Jqn85@T%w4tJmFA{oFotC$C4}U80mKKxLS*u1~?d zfi?OHA0R!|PceH%o#ZW9`;NT(M981%rzzgs#DBE05ELq{lmVp>Q$b}ENFdaLi6Oz1 z62UBq={(B?t+9g6oiHdUiIg^-3Y4Z^E!gQxm99$BW49EE?hQ-9deIBh2dTvY$MujS zzmN)|0dc@ZFM3>RHOr&Ok491HbGGc5x$vIZm)!A{_g$Giu6>^h8u+1K@ckEGc5Yx| zbEP4PkU9W~n6^}!D~L(7|IoRjKcBi~B@a=KS~s^~J=q@b&ZmdX6L@?+*&k2t&-ZA2 zFqG4i0Z7$;#%fELiv~Lb@`(gmr zbj^Y@t7d~XoouSA?1EEU;24&^Ya9ymP=CH_}RayQY0PU~1^HeOL}VJ!}M`|6h0 st=a_Pg_9dvy&fu4Iq65}29n*ySk}EzvzW7q%Buc*zUUZq^UbXN0!}kdLI3~& diff --git a/vm/stdlib/compiled/12/stdlib/043_BlockReward.mv b/vm/stdlib/compiled/12/stdlib/043_BlockReward.mv index 533de2359e99cadbee5359e8418a3d24c7ac3322..f2b272971c95f54f03e0c167c1b19ab91bce5520 100644 GIT binary patch delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ delta 547 zcmXYt&uddb5XX1+=i8m#_wwFLn^S>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk diff --git a/vm/stdlib/compiled/12/stdlib/044_Collection.mv b/vm/stdlib/compiled/12/stdlib/044_Collection.mv index 14cb6885c15c02e39d50fe39f2f4d45ea04ee280..306c01fc8c236d304380ad8666cf6810adb50368 100644 GIT binary patch delta 352 zcmW-cJx&8b42AtXzZuVNGP?nq2#G2MeKI770tpHvZh+t#bd*SRX(^Dn01cNwoB%F> z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~NETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfmcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= diff --git a/vm/stdlib/compiled/12/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/12/stdlib/051_EVMAddress.mv index 486a2ad6212e59a89d0807ca7c0d0c46a3600c74..9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7 100644 GIT binary patch delta 173 zcmYL>I|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS`3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;FtuZ%t;xHUCU0g`X=VWcnTQEj diff --git a/vm/stdlib/compiled/latest/stdlib/008_Vector.mv b/vm/stdlib/compiled/latest/stdlib/008_Vector.mv index ccd5d44b8211f0b9f46c6068eb77bcec872b8cba..fca0c13f9fa22089f35b632ccb13ea8f3c0c1427 100644 GIT binary patch delta 385 zcmYk1F-rqc49Ana_j>PMa@gAH2|XM+wx&KRM1N{RFhPb&7^wpUtJ?040;u7IIoUAORiSrY_{F?$ny)5}OUZ58 zR9_|wOUqj9pg)nGn!1**H?c0xZ6%)L%GXDBb7wg&3Rnt9sn}rWX>L#5aWcoYjY~bX h>0agCnKH4b9ulmnEhhm+r7SL_%C5Lf2MWJ01b;C>E3*Iq delta 394 zcmYk2u}T9$5Qb-FcklLg<}fiAPcfB|gn*>6nbN`+uo5I7h}ilPDp&~0^#MB@3oGBk z+R9cT;0xF|xgun$*>89M*>8V}&tmdCSa1da5rG`tpufZIFvsisfxa*wc5><&sID|hY~hEi3&o+Y z+tsfdkcOu6qnT1=AtAHlZEZ;idu8N83#K(nVJGFwpJ{8~U)U4OOp)_ieDce7=x5=i nT>APpwwe3-P>XPBG90Nwg1*h+GQm`F6<(XPc`|7-BPRFskE&~=THdY6H_WWp$Q;Y&^w-`mZ-Z8TAd|~Wi zNNWOfGDbix_pR9YE%Rfe-@|3nK#q srzjT#KLZ~l50f4@lLaFWGYc0hKQ{v-JEI_*IyX>?o7sYikqM{;02rndH~;_u delta 177 zcmdnYe34myq0DP;HUskE*lm%F4h1m_AbU$j`xfLZ2uWWxF$2P@yuju zVdR}VQO`)8laY~uiH(s9NOH6D07-UUK1K#sHg+}+HcmFKiTR3L>|C4-oD7UWa$2WhzF!M08aIx}p XGcd9<3bLtl1Esi`Em#6-6ljRNifCy#0F6rJ0P+$Ef{@@Tbt;YQ31}ta04||(1{}g} zCiyb+&tyLOp%0h%8yO<9AjPV#8#UW+WzvE5r5+Dwf-#)Kq6Fhrl%S&=Gp2pz46dKSyx~D6Yq)O2MG=pN2CHa zx-2?UR4_LsSdM?Yef`Kc5{ps954iOP)^x$M603f@zp@&3t&_yA^eT2E%oju^IMZ`VZJ$9@_u_ diff --git a/vm/stdlib/compiled/latest/stdlib/013_Option.mv b/vm/stdlib/compiled/latest/stdlib/013_Option.mv index 9a0dae2f25d1cc22a2f2e2ccde39c9a13bfcb2f2..340a58f50f415907a26ff143f03e2cab343beaba 100644 GIT binary patch delta 488 zcmYk3y-Gwe5QXO_H<``k?(W@P1;xTf?JV^w2(r31zJc#xAqa{fB8b*<3kxfsNd!fF z01?E(R&cWZrkHSYK9V_;H@TM8o0%8&08k=$@}AS4JC4N#9)(l7M|t3`f965_;Gk!Y z(z(0i@-a~RDO|4YP7m4z0{Kp^6VgXe=}IM0dYA9p^DXeP1(*xv;%#q1J_`Bh+ini= zzB!;B8cK6ZbNvk=LTjk6undVK*VrgeoeU+B60v!q$ev%V>6NyS1NkXMRR*`(BJNb&lB`?UOxMmt g+r(fZ!lDH#il{ctuT0jAP0KQoa_+!}WfKti1VVBw5dZ)H delta 575 zcmYjPJ8l#~5Uo#7cTM;7?9S}$tc@HX5;6h;+_r?|hyV_;;$sO3SzciiIN}5Zs}T_r zk`I6gi3kwffW$X&V9D$aaP0?AJ=yamFu4(J304ElKpC`vIW8fWvuIR~6+7I=i#zef z;mPUi!O5v!~Brm8&-=FUP1D3R;#4Nj-y`3>%*B0#C=10#cfnTjGMS7D%J z1Cq_ENxo8X#Y589}RkyVu&>dwH`et5*)ZgQlHgsutj04nm7FGvvs;h)x|ri4n> z^zDEC*W4c@?8f(ee$QkwvC98VllV4OIpb9B0qI8=Rmp2oYqA)A@vV0B#JPz&({%~l E0M8{mVgLXD diff --git a/vm/stdlib/compiled/latest/stdlib/014_BCS.mv b/vm/stdlib/compiled/latest/stdlib/014_BCS.mv index d58d5363cc266a5cbb3bec62584544ee56c164b7..d66fd2976727d10389ab808b938c050b7740b0c9 100644 GIT binary patch delta 656 zcmY*XJ!@1!6g_j_e7raFKK5<0pV?h8F}sP1ZazR8Wesa5SXl-MxJAGuN}@#?!G9o= zHbp=JmJ;j)@gG=NiA5lVg^k(^-nSbOZ*jQ8IrrRqXXZowW&Oob>$}qcA^}ZW2k|E% zzY3{N#i202>9q=v!jgU)F2`r#kCcT1L}UbK0q2ku0E#5ITmhyyhQy8$8NgBqIEsL{ zU=y-HN)E^hwnXMGkQZs0Vql6AV7fR~h+9>W0X39>dd^vca-dnjOaZL|+Fo`WunDo=W)#@NdiGCvUatPVIVSxf#{-0){&_9Ui#swbP<&o>@#K6<#j zx3M>p+dJDMxx2G9^6%w+x|8}u&4{jFR0mX<^-pbOe!$~QA~x(7%*nP&Eu73{tYV!Q zr-zyUsczSJd0y8M6Twa40t)%g>SGr>X#92Y>$*Y123NHFfnK5Jxqw%ngg-3=rLmx9 zw){}n?0{E4XAcdt`I!A|+M?xunRmtDZ^HRTx5xd(S~^WrtZuJbVX11t?6$aD}e2K`ayHa`(wOxlDWS41ECF9wg3PC delta 707 zcmY*Xy-!n76hG&@d+&SqzOOzCr7Z;oqHUo<1tV|K!Og@34bjPj6ljD9Ew)voi;0Va z#zd~0(dgh`K-@GMql24qcW^V&+1c}8FrML;bAIP{KJLf;;``#gSCwDB0sxKRk*A#K zCyGB-Kux+jdzj zQwsK_7{jW4E1qJ(UXle$(cX|HO36NwXA6!`e0@G8B5+dSJLK^jj2#N)o?@M1UBxb` zMI1k*UHdTOy#xNE+QErKUfYW<^X7nm;Ev-F``Uezt#H*lR;pHc45t1t<@u>6PZ=~+ zFu>VFPc*94?AIJvpwb|aMj4tGO*v_Rm8!snfe589($N0)%Kp@u{~GWKn|hv7-?sDu rrGb5}=Z4bIhm&qI;6D|j4~ty-U05(0z}7t1r#GqL2cBe$I; z99H`h-1f3Z9)K$^fW!gq8*t#jfmfi~Zcl=fIz-w?5iR+rzpAeK`s?a(&&zv%3y%;o zpd?_ykWU{wUo!WrBmXD*vlIO44#VHw_vLf%)fAj4VT2RMC&Y!zgNwlB0}g{t5U`Ta z+=Sy2BEf?m07~zf+kInyBP4`95QO?67z489$&iJNpEnu?6#6uvQQs19ms1KhN5pnRg;8ZauhJrqwe{Z4lPWz`o4(A|)3}YGtH^Xt*Cra!I#^}J zEXm@vS{LP6e4@&Dc2+5{>gr9z_Owi`$NoCaivd zOiWL@(Vz|}n6Kg`a4*W;g<2+cR>jGDUetLNn^D^r)rEnfl?h&@iPH)0r#gn%l?Ts- zIvv2{ILR_~wwJ*2mQ}n`xzd0p?TU|2>RFcRW4^<8 z`5nH;C!8`i<@=m5K7jHInI90&=v}C9Q|0fCJjXTrrD+oOfd{bd2qGPid_OZ=fk+On z!$4AZJn|(K)R_d5x%(ry3+6l!+=H#qw5jBtW8b|&_wrp62DaJeC9o#Jh2st1ZhJtc zb=(l~9>NARZA=(7>~=yj8VDk&U}mUrg(rLwh=B-&5K=@V_c37%T=`$@5H@bxB#Fp* z&~BO|=TjH(XC1p9-gP$vt^fZOw=$dy^_u6K*}~}SiRH!c+or!Q(M!5v{4pGx*LD4| zI5&;oG|zYKX71O|9R!~s_-|H-ew+yL$8@{t`QCJVY+j$)#C?4*J{bNMqUSb(Zy`9g zE(eJJHwZpM@DJ^0qCel<915ZJ@kcFu3I7wpKe7V#kuON)W!tj4+@9TwC`5}_^ zV+5aAIs0sWLHse*FqLnWn(%eMi_9xQeg({*1JnBYylUY~_y-7%<^2PxszW3WhCfIA zZLwYAxjB_}+unZNuZwply}mwJobII8_kRcBzl-44&5QBD;=C@NPZ0hXpIe2)uTR6r cF-Pn%WiAv8-MRX#Jti`0PU6v+jX5L#0&`?Gj{pDw literal 4003 zcmds4OK;>v5blTFesnvTnPfA0?6QvwAaO!~asX*j5NHo@0j&@RERUyWVy(v>`H>}i zSfTwLd*Lr2xO3pf4e<{+aNvN%FQD3EPlA(@h_sO+T5{X{Rdv-@)m=S3-`)OG3POlS ziO>82-~Y(^TG-RiobTyRmixCo@L!0x)eqw5T~MNg5l$>(JA?>$9C!#kE=Y*SVBmwC zNYg3HCL{nYdYgEJ-Z9;4(DA!IA?%(c)UoapMTzwwfa$&?ZDkYck`1AN3FBvt`Ywer zb!pI-xZLKHf{aq3m{8!v_Xu;K^N9QS)1y3_C3ENKI7+fdQ?VKbM|q(?oK6c}mbw&= zbzJ3z`XnkrTvkz5l}tai*RyUX^@uHeMI)ONLr1( zMUsV85oKkxVql!qK(5?PqhhZAC5!7SpUuV%Vd#>$q11UiSVZ3>i+T}e^wHp zjO`dV%OZ(&L$GtQ15+-6?-=+zsS@%Of&~*;Iqqgdb|8bPX{t|mB3R|33gyZoa+y_6-h2FF?fA70x~PbH4$+SO4hUyVbrkO z^2yMXL{iDjRB1~g9qCF>`cg_IgQ0y$7z0)Q2Ro#S1mw(E7v9i1WPm^KSoQF3xSrVR z|6lPo4QGjZ#qp-?hUn)L^NZm(E&uJ-ypT7HKZav*T~!~mbKUrL^L(>80pRrL0%`Z> zEd(DU_%H4JzMTNk@7(qEuRGrBwvWa2aqIZM#*6WMfaG%%!8Z^bTbBbw{|f}4AozQ5 z6Ev+KZ)UEKm*DTN;0yY{Bl!DBK3`tuR}#iyVfbIP{nf&D_d~?n2M9iH)7X#lM?@dX z7M9%`l|}ef-$&w#km@&oAWZA$=eHGn0e=U=b)|GpE zYF-uZZg&0rV0OBjU0?qVg#R{zU)3+h2eb34cs@e-V|;ED4yR7T$1+#y5oI=P7P@u$ R-Fie+(A>O(5gTzv{sC>GIamMy diff --git a/vm/stdlib/compiled/latest/stdlib/036_Authenticator.mv b/vm/stdlib/compiled/latest/stdlib/036_Authenticator.mv index 7fff119e122a9a7d9b41cf70cc56ef926141b43c..c5a74c50726f9630bc63c08e50f2b5271cbbcc8f 100644 GIT binary patch delta 396 zcmYk1u}T9$5Qb-FcW3Wr_u`!#f+h$Fh*($z1kcU{!9v8&RsxCyG=-?0jc*XsS(!Ib zu<|8*42k*zcFv{IVT<|qpZWIR8Be)8ZG8m>fPf$*)--nPzI6xN{vJOtnNiL^RLP4; z8ez=JITGJ$c8w;~qs4>_W#TE_he)y5N@3nk`)ENo61pYj)YSys4FjUIan} zNIMPav4^&0mwKz_ZvClwO&+Oz*yl1*hJp4t7}kw>Ek)z9fQ2Vb4hd)AQ3s$%$moS^ z2;>l*I8A2$h!1Avs840I3bYjj`UfN@!J!(EV}%uD=vk4dNL#;Q)BtjIYwMYkGzr2l DrMxEN delta 469 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 Wt!+_>wtHQZu92C_zG*FED*g*={3fsf diff --git a/vm/stdlib/compiled/latest/stdlib/040_Ring.mv b/vm/stdlib/compiled/latest/stdlib/040_Ring.mv index 868c648d3443124b11a5ee0c27a78862b3894682..870e433ee9fc90ba055fbebda1186a8e2267f49c 100644 GIT binary patch delta 709 zcmZ8f%W4%t5Ur}No|*2h>6y8iOa!B#7kt89NHBwlBr&>o<3>a`u7uy(606>jUP$6dal3$&($9RI} zXZnfN5x2!V_WlFM@R3PA@mpJeb4C2&avK5xFai*jieLcMFd0S5R!Vp-&LRXIh+ zUeO0Zz(>(qpr%ouTGTjo5*26CC>rb)*J950Q+9pG@nOg{(M2Calvu}Too!+pyBJf8 z&#oIR1EK>%er4@nYo<@{l-pq8Abr6_`i-wG9~>UMNXK+JeWxuSR^?*qWXxmQlk~)# zZ}&t@FqAiJ?|E3HXg1P&v-R}8ChT30f#-b7ZU?{;y6S3x+ZZq>wTrZ?;Q-sXE0a9k zup_G2bOf`QE0;WZm~&@cjU(<$y23Y?GbOvD+6qSP7jve3C!V%iZ8dYA+pfk3A{==n z-mGol(d`f39kAnZetKPj@Jqn85@T%w4tJmFA{oFotC$C4}U80mKKxLS*u1~?d zfi?OHA0R!|PceH%o#ZW9`;NT(M981%rzzgs#DBE05ELq{lmVp>Q$b}ENFdaLi6Oz1 z62UBq={(B?t+9g6oiHdUiIg^-3Y4Z^E!gQxm99$BW49EE?hQ-9deIBh2dTvY$MujS zzmN)|0dc@ZFM3>RHOr&Ok491HbGGc5x$vIZm)!A{_g$Giu6>^h8u+1K@ckEGc5Yx| zbEP4PkU9W~n6^}!D~L(7|IoRjKcBi~B@a=KS~s^~J=q@b&ZmdX6L@?+*&k2t&-ZA2 zFqG4i0Z7$;#%fELiv~Lb@`(gmr zbj^Y@t7d~XoouSA?1EEU;24&^Ya9ymP=CH_}RayQY0PU~1^HeOL}VJ!}M`|6h0 st=a_Pg_9dvy&fu4Iq65}29n*ySk}EzvzW7q%Buc*zUUZq^UbXN0!}kdLI3~& diff --git a/vm/stdlib/compiled/latest/stdlib/043_BlockReward.mv b/vm/stdlib/compiled/latest/stdlib/043_BlockReward.mv index 533de2359e99cadbee5359e8418a3d24c7ac3322..f2b272971c95f54f03e0c167c1b19ab91bce5520 100644 GIT binary patch delta 487 zcmYjNJ!@4#5S_ChcjxZx-cQj8G0BUG&lf)sjc^;mpoL(O+DaZMArK=7iXc{kq!1zO zFA%}PLJO-0mi_<>OABlL32EFsES%yDbLKEJXJ+BITzj|pM+zbm(1Kc6)SHjZdz`d0 zyn_3x4h(-Qn&L-s)xUO=@Yx;d`0;Rad(WKF1qxm&0i%}TNw0_yk3z2PDum>0tU$b0 zH!$ToGWv6E34>9WC1%_M1ORPF+$8 zASgG>r?=YU_yzMAnd}2%2dLI)xipQ61{>mI>}7+k0;KbUmSF2Sbfb`pzF#a^mGRek2CwqJe&AEh6heT$rs sHyEkKNhz_TfzvO0Wzw#gPw;vu1)@X`zC&Qiw+P=h*`A_oP%A}$0dtW#qW}N^ delta 547 zcmXYt&uddb5XX1+=i8m#_wwFLn^S>kYR14ERTA ziZAdQ)K7ZOlQ*g&&((GFRdwytEWYqw0x&}ya-8vwwHP$u>k0cj^E{a4D76 z9J$pg&veD3WfKe3-1EmR7$Uu`t3O#74g`~;*`_Ka^ zhNIKzS@=wMTJm@}-k%;+4#y7%$A|a&gUSAEG#pRO-pOQg@~A(Wg+KJM(WkQq2mQUl z-J@{Ep68w*@leohN~2GpNm$`4DW!R`No`?@3h^x5=IxH@82j(hi&*DdC_EK+Op%eu z;XkkW@RDy>)4@DraYE!SeB!q7Yqd>XiK!Q9=|e86t1rMf yEWHu0AQl=IaLiDlMpqDzVJ@z9t84rn80IYkQe@s0U=xg1F!9klP!dmJl#;*m)ktLk diff --git a/vm/stdlib/compiled/latest/stdlib/044_Collection.mv b/vm/stdlib/compiled/latest/stdlib/044_Collection.mv index 14cb6885c15c02e39d50fe39f2f4d45ea04ee280..306c01fc8c236d304380ad8666cf6810adb50368 100644 GIT binary patch delta 352 zcmW-cJx&8b42AtXzZuVNGP?nq2#G2MeKI770tpHvZh+t#bd*SRX(^Dn01cNwoB%F> z6hXle@Z!Q_&zpJv{O(`;>Us7po`@Py;?$YhmA&oU18%|=zlfh^;Lst<6vN z4b7MH?T_26FDoV@#HEQK?pg;jePY5?Z?U7v?oPZ)du5Q~D5u;;j^n)fSQZ$h5g`ae z56!QKxW?USWPkwICaPqu3xP_UCpxADDuj-}tia9+t*A1ap)o>hVhb&cP?3ZsPY-N# zjQ{~9&jkxQi>x5}Iwk delta 380 zcmX9)J5EC}5ZqbsvmJk4p5F_fB0{3d4HAtT8DQKt>&~g!s%I$Ytl5|3BWw1Fx_8t14?7J*J_iOP{qz^tJfiL|C_ z*o=dTv>_HKERjkISn=#we3LbUL&=MXB~@W{rfkP$x=_PF8B_*|6I#?p@_FPls+zpI jrjI-xRqWY~NETT z_N%;<*<0H;@9o0x|lZLhJRkRs>WL(u_8`7jUv?0Kj^Ho&d+M z@tBr&^;se^&x@tXfO2&lEpOz-A%1|1T(z)xK&U_fG}{iMR7&?eH_|lNa{M)IB$M z;BoibOy`dX%T#Nf7an2=GMC&q_c9jP|Bq58rTb~7M}4f4KIN%*__86$fL^jSI7rqU ieT{#>${FUVE#Z|^vQaoeYXSaRB9B{zq!-;&JNpCJ7&+kp delta 671 zcmZuuJ#Q2-5cP~dcGvdp-fV8O_pz6|B$wRrkw{4BVxkLlbaa$ZrbMEkp-O{9iO~K6 zN`xp-3PeE_XlVEgAW=a{LCg3;Ni%+)XTLYk{**r}PF^*?oDo8FKv=Y;Y<`1!fj9L9 z{)FVM`XbXmN!L72SN0+uyKkxTKhn>-^E%J-_xa0{H)qP$8U*YWg~0s*%iw%SAe0|v zTo+Jr=L$7kuSQl~+lsZQfIHV4uBzNB7~28MsNOW*t+r#oKVpmryNnHOQcZozXjqM^ zhQ_p2r8KFgRBzKxwM(;VkM>CsP)SI0TC{iJY=OpRLK*-_96%<3w_qGbn5-8};)(V< zj6FoS-e=-m;Ri7K^wHxdPtQNf23A3$;+W7svJjL~I&-X{X)w325?)(~dBxpbgNSz32$Q(GlCGN0cR0Zt>^=^w`G3Cz~51lyXw#OWUQ*{S} z@sLCCs?cSkSlL17I1D-4ac#fq!yBPMAH^mkD|p1(2_6hBUJm9papLt3H(mh36|dQvLjiz=VYj``yXaAGVMDvxnE-$`5n%E%t^pYk4~~RScuj< ha*tpC(ixUfwvQX}$}G2>+QUCfmcD|^_JXZ1;7jNu z2v#;0zJil04#}6p`O?gfd+`s?o8Ju+Q3N^bW^Tv%c?p+`_>Oh{Kx)76WYvR=Ua}R= zC_J?0il9LB3_^+tS_T-FdSHyTK+;!(0xXI2zsf`wyp>4)WVJeBufM)M9XdYl7R!DZ z@AlXDs=GN~Uhr8z4E_E1nq#4s1%<}XBPrNulR!e5c5%?kp^)lO_4J7O%8Pm~(;H6E YOxtFbgKH>IHcl5=Wn|<3v&n{j0mO?UN&o-= diff --git a/vm/stdlib/compiled/latest/stdlib/051_EVMAddress.mv b/vm/stdlib/compiled/latest/stdlib/051_EVMAddress.mv index 486a2ad6212e59a89d0807ca7c0d0c46a3600c74..9cb4ecebac2b0c410bd4a101d46305c5b3bb18f7 100644 GIT binary patch delta 173 zcmYL>I|{-;5I|>kXJ^CC21JY&##pF@O^8)ur?MCDB!aC+urUTJD@)JeHN*>e2|ozF z_VM0bA9Q|6pTYnj2&9N~l+Zw#;lzeA+O>F(&Yh6`8E+UrR9*Cm5OlrC>g)W7Az89F z&R7^zw)lL$G;2)FiaUXuE#~1byme&3ncj#hQ^a-#frS`3qve~6@*}6X=yD+f`OcMu{9AXdz}+T#On2FLaZXVnMRLZ1q?uow}s?P*g(yd!87s+DR;F SignedUserTransaction { + sender.sign_txn(RawUserTransaction::new_with_default_gas_token( + *sender.address(), + seq_num, + TransactionPayload::ScriptFunction(ScriptFunction::new( + ModuleId::new( + core_code_address(), + Identifier::new("TransferScripts").unwrap(), + ), + Identifier::new("peer_to_peer_v2").unwrap(), + vec![stc_type_tag()], + vec![ + bcs_ext::to_bytes(&recipient).unwrap(), + bcs_ext::to_bytes(&amount).unwrap(), + ], + )), + 10000000, + 1, + 1000 + 60 * 60, + net.chain_id(), + )) +} + //this only work for DEV or TEST pub fn create_signed_txn_with_association_account( payload: TransactionPayload, @@ -320,10 +350,7 @@ pub fn create_signed_txn_with_association_account( pub fn build_stdlib_package(net: &ChainNetwork, stdlib_option: StdLibOptions) -> Result { let init_script = match net.genesis_config().stdlib_version { StdlibVersion::Version(1) => build_init_script_v1(net), - StdlibVersion::Version(12) | StdlibVersion::Latest => { - build_init_script_with_function(net, "initialize_v3") - } - _ => build_init_script_with_function(net, "initialize_v2"), + _ => build_init_script_v2(net), }; stdlib_package(stdlib_option, Some(init_script)) } @@ -334,10 +361,7 @@ pub fn build_stdlib_package_with_modules( ) -> Result { let init_script = match net.genesis_config().stdlib_version { StdlibVersion::Version(1) => build_init_script_v1(net), - StdlibVersion::Version(12) | StdlibVersion::Latest => { - build_init_script_with_function(net, "initialize_v3") - } - _ => build_init_script_with_function(net, "initialize_v2"), + _ => build_init_script_v2(net), }; module_to_package(modules, Some(init_script)) } @@ -505,7 +529,7 @@ pub fn build_init_script_v1(net: &ChainNetwork) -> ScriptFunction { ) } -pub fn build_init_script_with_function(net: &ChainNetwork, function: &str) -> ScriptFunction { +pub fn build_init_script_v2(net: &ChainNetwork) -> ScriptFunction { let genesis_config = net.genesis_config(); let chain_id = net.chain_id().id(); let genesis_timestamp = net.genesis_block_parameter().timestamp; @@ -526,154 +550,147 @@ pub fn build_init_script_with_function(net: &ChainNetwork, function: &str) -> Sc let native_schedule = bcs_ext::to_bytes(&genesis_config.vm_config.gas_schedule.native_table) .expect("Cannot serialize gas schedule"); - let mut args = vec![ - bcs_ext::to_bytes(&net.genesis_config().stdlib_version.version()).unwrap(), - bcs_ext::to_bytes(&genesis_config.reward_delay).unwrap(), - bcs_ext::to_bytes(&G_TOTAL_STC_AMOUNT.scaling()).unwrap(), - bcs_ext::to_bytes(&genesis_config.pre_mine_amount).unwrap(), - bcs_ext::to_bytes(&genesis_config.time_mint_amount).unwrap(), - bcs_ext::to_bytes(&genesis_config.time_mint_period).unwrap(), - bcs_ext::to_bytes(&genesis_parent_hash.to_vec()).unwrap(), - bcs_ext::to_bytes(&association_auth_key).unwrap(), - bcs_ext::to_bytes(&genesis_auth_key).unwrap(), - bcs_ext::to_bytes(&chain_id).unwrap(), - bcs_ext::to_bytes(&genesis_timestamp).unwrap(), - //consensus config - bcs_ext::to_bytes(&genesis_config.consensus_config.uncle_rate_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.epoch_block_count).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_difficulty_window).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_reward_per_block).unwrap(), - bcs_ext::to_bytes( - &genesis_config - .consensus_config - .base_reward_per_uncle_percent, - ) - .unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.min_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.max_block_time_target).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_max_uncles_per_block).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_gas_limit).unwrap(), - bcs_ext::to_bytes(&genesis_config.consensus_config.strategy).unwrap(), - //vm config - bcs_ext::to_bytes(&genesis_config.publishing_option.is_script_allowed()).unwrap(), - bcs_ext::to_bytes( - &genesis_config - .publishing_option - .is_module_publishing_allowed(), - ) - .unwrap(), - bcs_ext::to_bytes(&instruction_schedule).unwrap(), - bcs_ext::to_bytes(&native_schedule).unwrap(), - //gas constants - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .global_memory_per_byte_cost, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .global_memory_per_byte_write_cost, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .min_transaction_gas_units, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .large_transaction_cutoff, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .intrinsic_gas_per_byte, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .maximum_number_of_gas_units, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .min_price_per_gas_unit, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .max_price_per_gas_unit, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .max_transaction_size_in_bytes, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .gas_unit_scaling_factor, - ) - .unwrap(), - bcs_ext::to_bytes( - &genesis_config - .vm_config - .gas_schedule - .gas_constants - .default_account_size, - ) - .unwrap(), - // dao config params - bcs_ext::to_bytes(&genesis_config.dao_config.voting_delay).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.voting_period).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.voting_quorum_rate).unwrap(), - bcs_ext::to_bytes(&genesis_config.dao_config.min_action_delay).unwrap(), - //transaction timeout config - bcs_ext::to_bytes(&genesis_config.transaction_timeout).unwrap(), - ]; - - if function == "initialize_v3" { - args.push( - bcs_ext::to_bytes(&GasSchedule::from(&genesis_config.vm_config.gas_schedule)).unwrap(), - ); - } - ScriptFunction::new( ModuleId::new(core_code_address(), Identifier::new("Genesis").unwrap()), - Identifier::new(function).unwrap(), + Identifier::new("initialize_v2").unwrap(), vec![], - args, + vec![ + bcs_ext::to_bytes(&net.genesis_config().stdlib_version.version()).unwrap(), + bcs_ext::to_bytes(&genesis_config.reward_delay).unwrap(), + bcs_ext::to_bytes(&G_TOTAL_STC_AMOUNT.scaling()).unwrap(), + bcs_ext::to_bytes(&genesis_config.pre_mine_amount).unwrap(), + bcs_ext::to_bytes(&genesis_config.time_mint_amount).unwrap(), + bcs_ext::to_bytes(&genesis_config.time_mint_period).unwrap(), + bcs_ext::to_bytes(&genesis_parent_hash.to_vec()).unwrap(), + bcs_ext::to_bytes(&association_auth_key).unwrap(), + bcs_ext::to_bytes(&genesis_auth_key).unwrap(), + bcs_ext::to_bytes(&chain_id).unwrap(), + bcs_ext::to_bytes(&genesis_timestamp).unwrap(), + //consensus config + bcs_ext::to_bytes(&genesis_config.consensus_config.uncle_rate_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.epoch_block_count).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_difficulty_window) + .unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_reward_per_block).unwrap(), + bcs_ext::to_bytes( + &genesis_config + .consensus_config + .base_reward_per_uncle_percent, + ) + .unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.min_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.max_block_time_target).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_max_uncles_per_block).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.base_block_gas_limit).unwrap(), + bcs_ext::to_bytes(&genesis_config.consensus_config.strategy).unwrap(), + //vm config + bcs_ext::to_bytes(&genesis_config.publishing_option.is_script_allowed()).unwrap(), + bcs_ext::to_bytes( + &genesis_config + .publishing_option + .is_module_publishing_allowed(), + ) + .unwrap(), + bcs_ext::to_bytes(&instruction_schedule).unwrap(), + bcs_ext::to_bytes(&native_schedule).unwrap(), + //gas constants + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .global_memory_per_byte_cost, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .global_memory_per_byte_write_cost, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .min_transaction_gas_units, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .large_transaction_cutoff, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .intrinsic_gas_per_byte, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .maximum_number_of_gas_units, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .min_price_per_gas_unit, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .max_price_per_gas_unit, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .max_transaction_size_in_bytes, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .gas_unit_scaling_factor, + ) + .unwrap(), + bcs_ext::to_bytes( + &genesis_config + .vm_config + .gas_schedule + .gas_constants + .default_account_size, + ) + .unwrap(), + // dao config params + bcs_ext::to_bytes(&genesis_config.dao_config.voting_delay).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.voting_period).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.voting_quorum_rate).unwrap(), + bcs_ext::to_bytes(&genesis_config.dao_config.min_action_delay).unwrap(), + //transaction timeout config + bcs_ext::to_bytes(&genesis_config.transaction_timeout).unwrap(), + ], ) } diff --git a/vm/types/Cargo.toml b/vm/types/Cargo.toml index ef7d5a8854..45a44f8412 100644 --- a/vm/types/Cargo.toml +++ b/vm/types/Cargo.toml @@ -52,7 +52,7 @@ edition = { workspace = true } license = { workspace = true } name = "starcoin-vm-types" publish = { workspace = true } -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } repository = { workspace = true } rust-version = { workspace = true } diff --git a/vm/types/src/account_config/constants/addresses.rs b/vm/types/src/account_config/constants/addresses.rs index a2cff2c7c8..2d2a0bee6d 100644 --- a/vm/types/src/account_config/constants/addresses.rs +++ b/vm/types/src/account_config/constants/addresses.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::account_address::AccountAddress; - pub use move_core_types::language_storage::CORE_CODE_ADDRESS; +use once_cell::sync::Lazy; pub fn association_address() -> AccountAddress { AccountAddress::from_hex_literal("0xA550C18") @@ -17,7 +17,35 @@ pub fn genesis_address() -> AccountAddress { CORE_CODE_ADDRESS } -pub fn table_handle_address() -> AccountAddress { - AccountAddress::from_hex_literal("0xA550C68") - .expect("Parsing valid hex literal should always succeed") +pub const TABLE_ADDRESS_LIST_LEN: usize = 32; +pub const TABLE_ADDRESS_LIST: [&str; TABLE_ADDRESS_LIST_LEN] = [ + "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", "0x38", "0x39", "0x3a", "0x3b", "0x3c", + "0x3d", "0x3e", "0x3f", "0x40", "0x41", "0x42", "0x43", "0x44", "0x45", "0x46", "0x47", "0x48", + "0x49", "0x4a", "0x4b", "0x4c", "0x4d", "0x4e", "0x4f", "0x50", +]; + +pub static TABLE_HANDLE_ADDRESS_LIST: Lazy> = Lazy::new(|| { + let mut arr = vec![]; + for str in TABLE_ADDRESS_LIST { + arr.push( + AccountAddress::from_hex_literal(str) + .expect("Parsing valid hex literal should always succeed"), + ); + } + arr +}); + +#[cfg(test)] +mod tests { + use crate::account_config::{TABLE_ADDRESS_LIST, TABLE_ADDRESS_LIST_LEN}; + use std::collections::HashSet; + + #[test] + fn test_table_handle_address_list_unique() { + let table_address_set: HashSet = TABLE_ADDRESS_LIST + .iter() + .map(|str| String::from(*str)) + .collect(); + assert_eq!(table_address_set.len(), TABLE_ADDRESS_LIST_LEN); + } } diff --git a/vm/types/src/on_chain_config/gas_schedule.rs b/vm/types/src/on_chain_config/gas_schedule.rs index 8643e44010..669b9dbb56 100644 --- a/vm/types/src/on_chain_config/gas_schedule.rs +++ b/vm/types/src/on_chain_config/gas_schedule.rs @@ -15,8 +15,6 @@ use std::collections::BTreeMap; const GAS_SCHEDULE_MODULE_NAME: &str = "GasSchedule"; pub static G_GAS_SCHEDULE_IDENTIFIER: Lazy = Lazy::new(|| Identifier::new(GAS_SCHEDULE_MODULE_NAME).unwrap()); -pub static G_GAS_SCHEDULE_INITIALIZE: Lazy = - Lazy::new(|| Identifier::new("initialize").unwrap()); pub static G_GAS_SCHEDULE_GAS_SCHEDULE: Lazy = Lazy::new(|| Identifier::new("gas_schedule").unwrap()); diff --git a/vm/types/src/on_chain_config/mod.rs b/vm/types/src/on_chain_config/mod.rs index 35c00f59e4..774525f3b2 100644 --- a/vm/types/src/on_chain_config/mod.rs +++ b/vm/types/src/on_chain_config/mod.rs @@ -32,7 +32,6 @@ pub use self::{ native_gas_schedule_v2, native_gas_schedule_v3, native_gas_schedule_v4, txn_gas_schedule_test, txn_gas_schedule_v1, txn_gas_schedule_v2, txn_gas_schedule_v3, GasSchedule, G_GAS_SCHEDULE_GAS_SCHEDULE, G_GAS_SCHEDULE_IDENTIFIER, - G_GAS_SCHEDULE_INITIALIZE, }, genesis_gas_schedule::{ instruction_table_v1, instruction_table_v2, native_table_v1, native_table_v2, diff --git a/vm/types/src/proptest_types.rs b/vm/types/src/proptest_types.rs index 6095a97054..78bff6b101 100644 --- a/vm/types/src/proptest_types.rs +++ b/vm/types/src/proptest_types.rs @@ -318,7 +318,7 @@ impl Arbitrary for RawUserTransaction { } impl SignatureCheckedTransaction { - // This isn't an Arbitrary impl because this doesn't generate *any* possible SignedTransaction, + // This isn't an Arbitrary impl because this doesn't generate *any* possible SignedUserTransaction, // just one kind of them. pub fn script_strategy( keypair_strategy: impl Strategy>, diff --git a/vm/types/src/state_store/state_key.rs b/vm/types/src/state_store/state_key.rs index ef5ad5ac62..c452ad6feb 100644 --- a/vm/types/src/state_store/state_key.rs +++ b/vm/types/src/state_store/state_key.rs @@ -1,9 +1,6 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) Aptos -// SPDX-License-Identifier: Apache-2.0 - use crate::access_path::AccessPath; use crate::state_store::table::TableHandle; use schemars::{self, JsonSchema}; diff --git a/vm/types/src/state_store/table.rs b/vm/types/src/state_store/table.rs index ec1871586d..fab209e847 100644 --- a/vm/types/src/state_store/table.rs +++ b/vm/types/src/state_store/table.rs @@ -1,9 +1,8 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) Aptos -// SPDX-License-Identifier: Apache-2.0 - +use crate::account_config::TABLE_ADDRESS_LIST_LEN; +use anyhow::format_err; use move_core_types::{ account_address::{AccountAddress, AccountAddressParseError}, language_storage::TypeTag, @@ -22,6 +21,15 @@ impl TableHandle { pub fn size(&self) -> usize { std::mem::size_of_val(&self.0) } + + pub fn get_idx(&self) -> Result { + let binding = self.0.into_bytes(); + let val = binding.last(); + match val { + Some(val) => Ok(*val as usize & (TABLE_ADDRESS_LIST_LEN - 1)), + _ => Err(format_err!("TableHandle array size > 0")), + } + } } impl FromStr for TableHandle { @@ -53,3 +61,25 @@ impl TableInfo { } } } + +#[cfg(test)] +mod tests { + use crate::state_store::table::TableHandle; + use move_core_types::account_address::AccountAddress; + + #[test] + fn test_get_idx() -> Result<(), anyhow::Error> { + let hdl1 = TableHandle(AccountAddress::ZERO); + let idx1 = hdl1.get_idx()?; + assert_eq!(idx1, 0); + + let hdl2 = TableHandle(AccountAddress::ONE); + let idx2 = hdl2.get_idx()?; + assert_eq!(idx2, 1); + + let hdl3 = TableHandle(AccountAddress::TWO); + let idx3 = hdl3.get_idx()?; + assert_eq!(idx3, 2); + Ok(()) + } +} diff --git a/vm/types/src/transaction/mod.rs b/vm/types/src/transaction/mod.rs index 34de8b6ec4..5a083a80ec 100644 --- a/vm/types/src/transaction/mod.rs +++ b/vm/types/src/transaction/mod.rs @@ -26,10 +26,12 @@ use starcoin_crypto::{ traits::*, HashValue, }; +use std::collections::BTreeMap; use std::ops::Deref; use std::{convert::TryFrom, fmt}; use crate::state_store::state_key::StateKey; +use crate::state_store::table::{TableHandle, TableInfo}; use crate::write_set::WriteOp; pub use error::CallError; pub use error::Error as TransactionError; @@ -471,7 +473,7 @@ impl fmt::Debug for SignedUserTransaction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "SignedTransaction {{ \n \ + "SignedUserTransaction {{ \n \ {{ raw_txn: {:#?}, \n \ authenticator: {:#?}, \n \ }} \n \ @@ -616,6 +618,9 @@ pub enum TransactionStatus { /// Keep the transaction output Keep(KeptVMStatus), + + /// Retry the transaction, e.g., after a reconfiguration + Retry, } impl TransactionStatus { @@ -623,6 +628,7 @@ impl TransactionStatus { match self { TransactionStatus::Keep(status) => Ok(status.clone()), TransactionStatus::Discard(code) => Err(*code), + TransactionStatus::Retry => Err(StatusCode::UNKNOWN_VALIDATION_STATUS), } } @@ -630,6 +636,7 @@ impl TransactionStatus { match self { TransactionStatus::Discard(_) => true, TransactionStatus::Keep(_) => false, + TransactionStatus::Retry => true, } } } @@ -646,6 +653,8 @@ impl From for TransactionStatus { /// The output of executing a transaction. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct TransactionOutput { + table_infos: BTreeMap, + write_set: WriteSet, /// The list of events emitted during this transaction. @@ -660,12 +669,14 @@ pub struct TransactionOutput { impl TransactionOutput { pub fn new( + table_infos: BTreeMap, write_set: WriteSet, events: Vec, gas_used: u64, status: TransactionStatus, ) -> Self { TransactionOutput { + table_infos, write_set, events, gas_used, @@ -677,6 +688,10 @@ impl TransactionOutput { &self.write_set } + pub fn table_infos(&self) -> &BTreeMap { + &self.table_infos + } + pub fn events(&self) -> &[ContractEvent] { &self.events } @@ -689,8 +704,22 @@ impl TransactionOutput { &self.status } - pub fn into_inner(self) -> (WriteSet, Vec, u64, TransactionStatus) { - (self.write_set, self.events, self.gas_used, self.status) + pub fn into_inner( + self, + ) -> ( + BTreeMap, + WriteSet, + Vec, + u64, + TransactionStatus, + ) { + ( + self.table_infos, + self.write_set, + self.events, + self.gas_used, + self.status, + ) } pub fn table_items(&self) -> Vec<(StateKey, WriteOp)> { diff --git a/vm/vm-runtime/Cargo.toml b/vm/vm-runtime/Cargo.toml index 1a720424c0..4cdb2832df 100644 --- a/vm/vm-runtime/Cargo.toml +++ b/vm/vm-runtime/Cargo.toml @@ -1,3 +1,14 @@ +[package] +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +name = "starcoin-vm-runtime" +publish = { workspace = true } +version = "1.13.7" +homepage = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } + [dependencies] anyhow = { workspace = true } bcs-ext = { package = "bcs-ext", workspace = true } @@ -20,6 +31,9 @@ starcoin-metrics = { optional = true, workspace = true } starcoin-gas = { workspace = true } starcoin-gas-algebra-ext = { workspace = true } serde = { features = ["derive"], workspace = true } +starcoin-parallel-executor = { workspace = true } +rayon = { workspace = true } +num_cpus = { workspace = true } [dev-dependencies] stdlib = { package = "stdlib", workspace = true } @@ -28,14 +42,3 @@ stdlib = { package = "stdlib", workspace = true } default = ["metrics"] metrics = ["starcoin-metrics"] testing = ["move-stdlib/testing", "starcoin-natives/testing"] - -[package] -authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } -name = "starcoin-vm-runtime" -publish = { workspace = true } -version = "1.13.5" -homepage = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } diff --git a/vm/vm-runtime/src/data_cache.rs b/vm/vm-runtime/src/data_cache.rs index b38de555bb..f380f71410 100644 --- a/vm/vm-runtime/src/data_cache.rs +++ b/vm/vm-runtime/src/data_cache.rs @@ -135,6 +135,7 @@ impl<'a, S: StateView> ResourceResolver for RemoteStorage<'a, S> { self.get(&ap).map_err(|e| e.finish(Location::Undefined)) } } + // TODO Note for Conflicting: conflicting implementation in crate `starcoin_vm_types`: - impl ConfigStorage for V where V: StateView; // impl<'a, S: StateView> ConfigStorage for RemoteStorage<'a, S> { // fn fetch_config(&self, access_path: AccessPath) -> Option> { diff --git a/vm/vm-runtime/src/lib.rs b/vm/vm-runtime/src/lib.rs index 7b3bcc534d..2680fefa62 100644 --- a/vm/vm-runtime/src/lib.rs +++ b/vm/vm-runtime/src/lib.rs @@ -1,19 +1,43 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 +mod adapter_common; pub mod data_cache; #[cfg(feature = "metrics")] pub mod metrics; pub mod natives; pub mod starcoin_vm; -pub use move_vm_runtime::move_vm; -pub use move_vm_runtime::session; + +use move_core_types::vm_status::VMStatus; +pub use move_vm_runtime::{move_vm, session}; mod access_path_cache; mod errors; pub mod move_vm_ext; -use starcoin_vm_types::access_path::AccessPath; -use starcoin_vm_types::account_address::AccountAddress; -use starcoin_vm_types::language_storage::StructTag; +pub mod parallel_executor; +use crate::metrics::VMMetrics; +use starcoin_vm_types::{ + access_path::AccessPath, + account_address::AccountAddress, + language_storage::StructTag, + state_view::StateView, + transaction::{Transaction, TransactionOutput}, +}; + +/// This trait describes the VM's execution interface. +pub trait VMExecutor: Send + Sync { + // NOTE: At the moment there are no persistent caches that live past the end of a block (that's + // why execute_block doesn't take &self.) + // There are some cache invalidation issues around transactions publishing code that need to be + // sorted out before that's possible. + + /// Executes a block of transactions and returns output for each one of them. + fn execute_block( + transactions: Vec, + state_view: &impl StateView, + block_gas_limit: Option, + metrics: Option, + ) -> Result, VMStatus>; +} /// Get the AccessPath to a resource stored under `address` with type name `tag` fn create_access_path(address: AccountAddress, tag: StructTag) -> AccessPath { diff --git a/vm/vm-runtime/src/move_vm_ext/session.rs b/vm/vm-runtime/src/move_vm_ext/session.rs index c427d4c188..57c3b8aa42 100644 --- a/vm/vm-runtime/src/move_vm_ext/session.rs +++ b/vm/vm-runtime/src/move_vm_ext/session.rs @@ -13,9 +13,11 @@ use starcoin_vm_types::block_metadata::BlockMetadata; use starcoin_vm_types::contract_event::ContractEvent; use starcoin_vm_types::event::EventKey; use starcoin_vm_types::state_store::state_key::StateKey; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use starcoin_vm_types::transaction::SignatureCheckedTransaction; use starcoin_vm_types::transaction_metadata::TransactionMetadata; use starcoin_vm_types::write_set::{WriteOp, WriteSet, WriteSetMut}; +use std::collections::BTreeMap; use std::convert::TryFrom; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash)] @@ -77,7 +79,14 @@ impl SessionOutput { pub fn into_change_set( self, ap_cache: &mut C, - ) -> Result<(WriteSet, Vec), VMStatus> { + ) -> Result< + ( + BTreeMap, + WriteSet, + Vec, + ), + VMStatus, + > { let Self { change_set, events, @@ -127,6 +136,14 @@ impl SessionOutput { } } + let mut table_infos = BTreeMap::new(); + for (key, value) in table_change_set.new_tables { + let handle = TableHandle(key.0); + let info = TableInfo::new(value.key_type, value.value_type); + + table_infos.insert(handle, info); + } + let write_set = write_set_mut .freeze() .map_err(|_| VMStatus::Error(StatusCode::DATA_FORMAT_ERROR))?; @@ -140,6 +157,6 @@ impl SessionOutput { }) .collect::, VMStatus>>()?; - Ok((write_set, events)) + Ok((table_infos, write_set, events)) } } diff --git a/vm/vm-runtime/src/starcoin_vm.rs b/vm/vm-runtime/src/starcoin_vm.rs index 48a3088938..c9402e5f51 100644 --- a/vm/vm-runtime/src/starcoin_vm.rs +++ b/vm/vm-runtime/src/starcoin_vm.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::access_path_cache::AccessPathCache; +use crate::adapter_common::{ + discard_error_output, discard_error_vm_status, PreprocessedTransaction, VMAdapter, +}; use crate::data_cache::{AsMoveResolver, RemoteStorage, StateViewCache}; use crate::errors::{ convert_normal_success_epilogue_error, convert_prologue_runtime_error, error_split, @@ -12,6 +15,8 @@ use move_core_types::gas_algebra::{InternalGasPerByte, NumBytes}; use move_table_extension::NativeTableContext; use move_vm_runtime::move_vm_adapter::{PublishModuleBundleOption, SessionAdapter}; use move_vm_runtime::session::Session; +use num_cpus; +use once_cell::sync::OnceCell; use starcoin_config::genesis_config::G_LATEST_GAS_PARAMS; use starcoin_crypto::HashValue; use starcoin_gas::{NativeGasParameters, StarcoinGasMeter, StarcoinGasParameters}; @@ -30,7 +35,6 @@ use starcoin_types::{ SignatureCheckedTransaction, SignedUserTransaction, Transaction, TransactionOutput, TransactionPayload, TransactionStatus, }, - write_set::WriteSet, }; use starcoin_vm_types::access::{ModuleAccess, ScriptAccess}; use starcoin_vm_types::account_address::AccountAddress; @@ -39,15 +43,15 @@ use starcoin_vm_types::account_config::{ core_code_address, genesis_address, ModuleUpgradeStrategy, TwoPhaseUpgradeV2Resource, G_EPILOGUE_NAME, G_EPILOGUE_V2_NAME, G_PROLOGUE_NAME, }; +use starcoin_vm_types::errors::VMResult; use starcoin_vm_types::file_format::{CompiledModule, CompiledScript}; use starcoin_vm_types::gas_schedule::G_LATEST_GAS_COST_TABLE; use starcoin_vm_types::genesis_config::StdlibVersion; use starcoin_vm_types::identifier::IdentStr; use starcoin_vm_types::language_storage::ModuleId; use starcoin_vm_types::on_chain_config::{ - GasSchedule, MoveLanguageVersion, G_GAS_CONSTANTS_IDENTIFIER, G_GAS_SCHEDULE_GAS_SCHEDULE, - G_GAS_SCHEDULE_IDENTIFIER, G_INSTRUCTION_SCHEDULE_IDENTIFIER, G_NATIVE_SCHEDULE_IDENTIFIER, - G_VM_CONFIG_IDENTIFIER, + GasSchedule, MoveLanguageVersion, G_GAS_CONSTANTS_IDENTIFIER, + G_INSTRUCTION_SCHEDULE_IDENTIFIER, G_NATIVE_SCHEDULE_IDENTIFIER, G_VM_CONFIG_IDENTIFIER, }; use starcoin_vm_types::state_store::state_key::StateKey; use starcoin_vm_types::state_view::StateReaderExt; @@ -63,10 +67,14 @@ use starcoin_vm_types::{ transaction_metadata::TransactionMetadata, vm_status::{StatusCode, VMStatus}, }; +use std::cmp::min; use std::sync::Arc; +static EXECUTION_CONCURRENCY_LEVEL: OnceCell = OnceCell::new(); + #[cfg(feature = "metrics")] use crate::metrics::VMMetrics; +use crate::VMExecutor; #[derive(Clone)] #[allow(clippy::upper_case_acronyms)] @@ -179,7 +187,7 @@ impl StarcoinVM { < StdlibVersion::Version(VMCONFIG_UPGRADE_VERSION_MARK) { debug!( - "stdlib version: {}, fetch vmconfig from onchain resource", + "stdlib version: {}, fetch VMConfig from onchain resource", stdlib_version ); let gas_cost_table = VMConfig::fetch_config(&remote_storage)? @@ -187,13 +195,13 @@ impl StarcoinVM { .gas_schedule; ( Some(GasSchedule::from(&gas_cost_table)), - "gas schedule from gensis", + "gas schedule from VMConfig", ) } else if stdlib_version >= StdlibVersion::Version(VMCONFIG_UPGRADE_VERSION_MARK) && stdlib_version < StdlibVersion::Version(GAS_SCHEDULE_UPGRADE_VERSION_MARK) { debug!( - "stdlib version: {}, fetch vmconfig from onchain module", + "stdlib version: {}, fetch VMConfig from onchain module", stdlib_version ); let instruction_schedule = { @@ -253,33 +261,15 @@ impl StarcoinVM { }; ( Some(GasSchedule::from(&cost_table)), - "gas schedule from vm config", + "gas schedule from VMConfig", ) } else { debug!( - "stdlib version: {}, fetch gas schedule from onchain module", + "stdlib version: {}, fetch schedule from onchain module GasSchedule", stdlib_version ); - let gas_schedule = { - let data = self - .execute_readonly_function_internal( - state, - &ModuleId::new( - core_code_address(), - G_GAS_SCHEDULE_IDENTIFIER.to_owned(), - ), - G_GAS_SCHEDULE_GAS_SCHEDULE.as_ident_str(), - vec![], - vec![], - false, - )? - .pop() - .ok_or_else(|| { - anyhow::anyhow!("Expect 0x1::GasSchedule::initialize() return value") - })?; - bcs_ext::from_bytes::(&data)? - }; - (Some(gas_schedule), "gas schedule from gas schedule in move") + let gas_schedule = GasSchedule::fetch_config(&remote_storage)?; + (gas_schedule, "gas schedule from GasSchedule") }; #[cfg(feature = "print_gas_info")] match self.gas_schedule.as_ref() { @@ -924,7 +914,6 @@ impl StarcoinVM { storage: &S, txn: SignedUserTransaction, ) -> (VMStatus, TransactionOutput) { - let txn_id = txn.id(); let txn_data = match TransactionMetadata::new(&txn) { Ok(txn_data) => txn_data, Err(e) => { @@ -972,7 +961,7 @@ impl StarcoinVM { match result { Ok(status_and_output) => { log_vm_status( - txn_id, + txn.id(), &txn_data, &status_and_output.0, Some(&status_and_output.1), @@ -981,7 +970,7 @@ impl StarcoinVM { } Err(err) => { let txn_status = TransactionStatus::from(err.clone()); - log_vm_status(txn_id, &txn_data, &err, None); + log_vm_status(txn.id(), &txn_data, &err, None); if txn_status.is_discarded() { discard_error_vm_status(err) } else { @@ -1063,8 +1052,6 @@ impl StarcoinVM { Ok(()) } - /// XXX FIXME YSG add delta_change_set for TransactionOutput - /// Execute a block transactions with gas_limit, /// if gas is used up when executing some txn, only return the outputs of previous succeed txns. pub fn execute_block_transactions( @@ -1072,12 +1059,13 @@ impl StarcoinVM { storage: &S, transactions: Vec, block_gas_limit: Option, - ) -> Result> { + ) -> Result, VMStatus> { let mut data_cache = StateViewCache::new(storage); let mut result = vec![]; // TODO load config by config change event - self.load_configs(&data_cache)?; + self.load_configs(&data_cache) + .map_err(|_err| VMStatus::Error(StatusCode::STORAGE_ERROR))?; let mut gas_left = block_gas_limit.unwrap_or(u64::MAX); @@ -1116,7 +1104,8 @@ impl StarcoinVM { data_cache.push_write_set(output.write_set()) } // TODO load config by config change event - self.check_reconfigure(&data_cache, &output)?; + self.check_reconfigure(&data_cache, &output) + .map_err(|_err| VMStatus::Error(StatusCode::STORAGE_ERROR))?; #[cfg(feature = "metrics")] if let Some(timer) = timer { @@ -1265,7 +1254,9 @@ impl StarcoinVM { let table_change_set = table_context .into_change_set() .map_err(|e| e.finish(Location::Undefined))?; - let (write_set, _events) = SessionOutput { + // Ignore new table infos. + // No table infos should be produced in readonly function. + let (_table_infos, write_set, _events) = SessionOutput { change_set, events, table_change_set, @@ -1335,6 +1326,7 @@ impl StarcoinVM { TransactionStatus::Discard(status) => { (VMStatus::Error(status), discard_error_output(status)) } + TransactionStatus::Retry => unreachable!(), } } @@ -1344,6 +1336,43 @@ impl StarcoinVM { VMStatus::Error(StatusCode::VM_STARTUP_FAILURE) }) } + + /// Sets execution concurrency level when invoked the first time. + pub fn set_concurrency_level_once(mut concurrency_level: usize) { + concurrency_level = min(concurrency_level, num_cpus::get()); + // Only the first call succeeds, due to OnceCell semantics. + EXECUTION_CONCURRENCY_LEVEL.set(concurrency_level).ok(); + info!("TurboSTM executor concurrency_level {}", concurrency_level); + } + + /// Get the concurrency level if already set, otherwise return default 1 + /// (sequential execution). + pub fn get_concurrency_level() -> usize { + match EXECUTION_CONCURRENCY_LEVEL.get() { + Some(concurrency_level) => *concurrency_level, + None => 1, + } + } + + /// Alternate form of 'execute_block' that keeps the vm_status before it goes into the + /// `TransactionOutput` + pub fn execute_block_and_keep_vm_status( + txns: Vec, + state_view: &impl StateView, + block_gas_limit: Option, + metrics: Option, + ) -> Result, VMStatus> { + let mut vm = StarcoinVM::new(metrics); + vm.execute_block_transactions(state_view, txns, block_gas_limit) + } + + pub fn load_module<'r, R: MoveResolverExt>( + &self, + module_id: &ModuleId, + remote: &'r R, + ) -> VMResult> { + self.move_vm.load_module(module_id, remote) + } } #[allow(clippy::large_enum_variant)] @@ -1361,6 +1390,7 @@ impl TransactionBlock { } } +/// TransactionBlock::UserTransaction | TransactionBlock::BlockPrologue | TransactionBlock::UserTransaction pub fn chunk_block_transactions(txns: Vec) -> Vec { let mut blocks = vec![]; let mut buf = vec![]; @@ -1403,30 +1433,6 @@ pub(crate) fn charge_global_write_gas_usage( .map_err(|p_err| p_err.finish(Location::Undefined).into_vm_status()) } -pub(crate) fn discard_error_vm_status(err: VMStatus) -> (VMStatus, TransactionOutput) { - info!("discard error vm_status output: {:?}", err); - let vm_status = err.clone(); - let error_code = match err.keep_or_discard() { - Ok(_) => { - debug_assert!(false, "discarding non-discardable error: {:?}", vm_status); - vm_status.status_code() - } - Err(code) => code, - }; - (vm_status, discard_error_output(error_code)) -} - -pub(crate) fn discard_error_output(err: StatusCode) -> TransactionOutput { - info!("discard error output: {:?}", err); - // Since this transaction will be discarded, no writeset will be included. - TransactionOutput::new( - WriteSet::default(), - vec![], - 0, - TransactionStatus::Discard(err), - ) -} - pub(crate) fn get_transaction_output( ap_cache: &mut A, session: SessionAdapter, @@ -1444,13 +1450,14 @@ pub(crate) fn get_transaction_output( let table_change_set = table_context .into_change_set() .map_err(|e| e.finish(Location::Undefined))?; - let (write_set, events) = SessionOutput { + let (table_infos, write_set, events) = SessionOutput { change_set, events, table_change_set, } .into_change_set(ap_cache)?; Ok(TransactionOutput::new( + table_infos, write_set, events, u64::from(gas_used), @@ -1493,3 +1500,93 @@ pub fn log_vm_status( } } } + +// Executor external API +impl VMExecutor for StarcoinVM { + /// Execute a block of `transactions`. The output vector will have the exact same length as the + /// input vector. The discarded transactions will be marked as `TransactionStatus::Discard` and + /// have an empty `WriteSet`. Also `state_view` is immutable, and does not have interior + /// mutability. Writes to be applied to the data view are encoded in the write set part of a + /// transaction output. + fn execute_block( + transactions: Vec, + state_view: &impl StateView, + block_gas_limit: Option, + metrics: Option, + ) -> Result, VMStatus> { + let concurrency_level = Self::get_concurrency_level(); + if concurrency_level > 1 { + let (result, _) = crate::parallel_executor::ParallelStarcoinVM::execute_block( + transactions, + state_view, + concurrency_level, + block_gas_limit, + metrics, + )?; + // debug!("TurboSTM executor concurrency_level {}", concurrency_level); + Ok(result) + } else { + let output = Self::execute_block_and_keep_vm_status( + transactions, + state_view, + block_gas_limit, + metrics, + )?; + Ok(output + .into_iter() + .map(|(_vm_status, txn_output)| txn_output) + .collect()) + } + } +} + +impl VMAdapter for StarcoinVM { + fn new_session<'r, R: MoveResolverExt>( + &self, + remote: &'r R, + session_id: SessionId, + ) -> SessionAdapter<'r, '_, R> { + self.move_vm.new_session(remote, session_id).into() + } + + fn check_signature(txn: SignedUserTransaction) -> Result { + txn.check_signature() + } + + fn should_restart_execution(output: &TransactionOutput) -> bool { + // XXX FIXME YSG if GasSchedule.move UpgradeEvent + for event in output.events() { + if event.key().get_creator_address() == genesis_address() + && (event.is::() || event.is::>()) + { + info!("should_restart_execution happen"); + return true; + } + } + false + } + + fn execute_single_transaction( + &self, + txn: &PreprocessedTransaction, + data_cache: &S, + ) -> Result<(VMStatus, TransactionOutput, Option), VMStatus> { + Ok(match txn { + PreprocessedTransaction::UserTransaction(txn) => { + let sender = txn.sender().to_string(); + let (vm_status, output) = self.execute_user_transaction(data_cache, *txn.clone()); + // XXX FIXME YSG + // let gas_unit_price = transaction.gas_unit_price(); think about gas_used OutOfGas + (vm_status, output, Some(sender)) + } + PreprocessedTransaction::BlockMetadata(block_meta) => { + let (vm_status, output) = + match self.process_block_metadata(data_cache, block_meta.clone()) { + Ok(output) => (VMStatus::Executed, output), + Err(vm_status) => discard_error_vm_status(vm_status), + }; + (vm_status, output, Some("block_meta".to_string())) + } + }) + } +} diff --git a/vm/vm-status-translator/Cargo.toml b/vm/vm-status-translator/Cargo.toml index b97f078fe6..ac17b4b4fe 100644 --- a/vm/vm-status-translator/Cargo.toml +++ b/vm/vm-status-translator/Cargo.toml @@ -9,7 +9,7 @@ starcoin-vm-types = { workspace = true } authors = { workspace = true } edition = { workspace = true } name = "vm-status-translator" -version = "1.13.5" +version = "1.13.7" homepage = { workspace = true } license = { workspace = true } publish = { workspace = true } From 6d9da9c323588b93b327d3f62aca53c52f614388 Mon Sep 17 00:00:00 2001 From: Jack Huang Date: Tue, 17 Oct 2023 04:27:34 -0500 Subject: [PATCH 10/17] check if dag or not to try to connect the block (#3976) --- .../src/block_connector/test_illegal_block.rs | 22 +++---- sync/src/block_connector/write_block_chain.rs | 65 ++++--------------- 2 files changed, 25 insertions(+), 62 deletions(-) diff --git a/sync/src/block_connector/test_illegal_block.rs b/sync/src/block_connector/test_illegal_block.rs index 808d4d04dc..2afd8a437a 100644 --- a/sync/src/block_connector/test_illegal_block.rs +++ b/sync/src/block_connector/test_illegal_block.rs @@ -123,7 +123,7 @@ fn apply_with_illegal_uncle( .current_header() .id(); let mut main = BlockChain::new(net.time_service(), head_id, storage, None)?; - main.apply(new_block.clone(), None, &mut None)?; + main.apply(new_block.clone(), None)?; Ok(new_block) } @@ -160,7 +160,7 @@ async fn test_verify_gas_limit(succ: bool) -> Result<()> { .with_gas_used(u64::MAX) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -183,7 +183,7 @@ async fn test_verify_body_hash(succ: bool) -> Result<()> { .with_body_hash(HashValue::random()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -206,7 +206,7 @@ async fn test_verify_parent_id(succ: bool) -> Result<()> { .with_parent_hash(HashValue::random()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -234,7 +234,7 @@ async fn test_verify_timestamp(succ: bool) -> Result<()> { .with_timestamp(main.current_header().timestamp()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -263,7 +263,7 @@ async fn test_verify_future_timestamp(succ: bool) -> Result<()> { ) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -491,7 +491,7 @@ async fn test_verify_state_root(succ: bool) -> Result<()> { .with_state_root(HashValue::random()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -510,7 +510,7 @@ async fn test_verify_block_used_gas(succ: bool) -> Result<()> { if !succ { new_block.header = new_block.header().as_builder().with_gas_used(1).build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -548,7 +548,7 @@ async fn test_verify_accumulator_root(succ: bool) -> Result<()> { .with_accumulator_root(HashValue::random()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -571,7 +571,7 @@ async fn test_verify_block_accumulator_root(succ: bool) -> Result<()> { .with_parent_block_accumulator_root(HashValue::random()) .build(); } - main.apply(new_block, None, &mut None)?; + main.apply(new_block, None)?; Ok(()) } @@ -602,7 +602,7 @@ async fn test_verify_block_number_failed(succ: bool, order: bool) { .build(); } } - let apply_failed = main.apply(new_block, None, &mut None); + let apply_failed = main.apply(new_block, None); if !succ { assert!(apply_failed.is_err()); if let Err(apply_err) = apply_failed { diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 9e6021b7c2..2f26560c15 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -100,13 +100,17 @@ impl

WriteableChainService for WriteBlockChainService

where P: TxPoolSyncService + 'static, { - fn try_connect(&mut self, block: Block, tips_headers: Option>) -> Result<()> { + fn try_connect(&mut self, block: Block, parents_hash: Option>) -> Result<()> { let _timer = self .metrics .as_ref() .map(|metrics| metrics.chain_block_connect_time.start_timer()); - let result = self.connect_inner(block, tips_headers); + let result = if parents_hash.is_some() { + self.connect_dag_inner(block, parents_hash) + } else { + self.connect_inner(block) + }; if let Some(metrics) = self.metrics.as_ref() { let result = match result.as_ref() { @@ -826,7 +830,6 @@ where fn connect_inner( &mut self, block: Block, - tips_headers: Option>, ) -> Result { let block_id = block.id(); if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH @@ -839,55 +842,15 @@ where debug!("Repeat connect, current header is {} already.", block_id); return Ok(ConnectOk::MainDuplicate); } - - // if it received a block with tips, it is a dag block - if let Some(dag_block_parents) = tips_headers { - // tips header, check the dag time window to see if it is should apply the blocks - // checkout if it is time to settle down - let time_service = DagBlockTimeWindowService::new( - 3 * 1000000, - self.config.net().time_service().clone(), - ); - let block_id = block.header.id(); - self.dag_block_pool - .lock() - .unwrap() - .push((block, dag_block_parents.clone())); - - let _testing = self - .dag - .lock() - .unwrap() - .push_parent_children(block_id, Arc::new(dag_block_parents.clone())); - - // if self.dag_block_pool.lock().unwrap().len() < 3 { - // // TimeWindowResult::InTimeWindow => { - // return Ok(ConnectOk::DagPending); - // } else { - // TimeWindowResult::BeforeTimeWindow => { - // return Err(ConnectBlockError::DagBlockBeforeTimeWindow(Box::new(block)).into()) - // } - // TimeWindowResult::AfterTimeWindow => { - // dump the block in the time window pool and put the block into the next time window pool - // self.main.status().tips_hash = None; // set the tips to None, and in connect_to_main, the block will be added to the tips - - // 2, get the new tips and clear the blocks in the pool - let dag_blocks = self.dag_block_pool.lock().unwrap().clone(); - self.dag_block_pool.lock().unwrap().clear(); - - return self.execute_dag_block_in_pool(dag_blocks, dag_block_parents); - // } - } else { - // normal block, just connect to main - let mut next_tips = Some(vec![]); - let executed_block = self - .connect_to_main(block, None, None, &mut next_tips)? - .clone(); - if let Some(block) = executed_block.block() { - self.broadcast_new_head(block.clone(), None, None); - } - return Ok(executed_block); + // normal block, just connect to main + let mut next_tips = Some(vec![]); + let executed_block = self + .connect_to_main(block, None, None, &mut next_tips)? + .clone(); + if let Some(block) = executed_block.block() { + self.broadcast_new_head(block.clone(), None, None); } + return Ok(executed_block); } #[cfg(test)] From d2081524d5c9d19d24f06547f2ccc9e94e331857 Mon Sep 17 00:00:00 2001 From: Jack Huang Date: Tue, 17 Oct 2023 04:33:39 -0500 Subject: [PATCH 11/17] check if dag or not to try to connect the main (#3977) * check if dag or not to try to connect the block * fix compile error --- sync/src/block_connector/write_block_chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 2f26560c15..40d58a51fb 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -107,7 +107,7 @@ where .map(|metrics| metrics.chain_block_connect_time.start_timer()); let result = if parents_hash.is_some() { - self.connect_dag_inner(block, parents_hash) + self.connect_dag_inner(block, parents_hash.expect("parents_hash should not be None")) } else { self.connect_inner(block) }; From 5dc52fa587ab2446b7530a07b51cf385728999aa Mon Sep 17 00:00:00 2001 From: Jack Huang Date: Thu, 19 Oct 2023 04:45:57 -0500 Subject: [PATCH 12/17] Generate the block for single chain if the number of the block is smaller than the dag one (#3979) * check if dag or not to try to connect the block * fix compile error * add adaptive for single chain * remove the dag code in single connection block --- Cargo.lock | 1 + benchmarks/src/chain.rs | 10 +- block-relayer/src/block_relayer.rs | 11 +- chain/api/Cargo.toml | 1 + chain/api/src/chain.rs | 8 +- chain/mock/src/mock_chain.rs | 18 +- chain/service/src/chain_service.rs | 12 +- chain/src/chain.rs | 99 ++++-- cmd/db-exporter/src/main.rs | 48 ++- cmd/replay/src/main.rs | 11 +- genesis/src/lib.rs | 5 +- miner/src/create_block_template/mod.rs | 14 +- .../test_create_block_template.rs | 57 +++- network-rpc/src/rpc.rs | 9 +- network/api/src/messages.rs | 12 +- node/src/network_service_factory.rs | 2 +- node/src/node.rs | 13 +- rpc/server/src/module/pubsub/tests.rs | 8 +- storage/src/lib.rs | 77 ++++- storage/src/upgrade.rs | 6 + .../block_connector_service.rs | 3 +- .../src/block_connector/test_illegal_block.rs | 24 +- .../block_connector/test_write_block_chain.rs | 1 + .../test_write_dag_block_chain.rs | 4 +- sync/src/block_connector/write_block_chain.rs | 296 ++++++++---------- sync/src/sync.rs | 6 +- sync/src/tasks/block_sync_task.rs | 83 +++-- sync/src/tasks/inner_sync_task.rs | 5 + sync/src/tasks/mod.rs | 3 + sync/src/tasks/sync_dag_full_task.rs | 5 + sync/src/tasks/tests.rs | 3 + test-helper/src/chain.rs | 10 +- types/src/system_events.rs | 7 +- .../compiled/12/11-12/stdlib/052_Epoch.mv | Bin 2724 -> 0 bytes .../compiled/12/11-12/stdlib/053_EventUtil.mv | Bin 490 -> 0 bytes .../12/11-12/stdlib/054_FixedPoint32.mv | Bin 595 -> 0 bytes .../12/11-12/stdlib/055_GasSchedule.mv | Bin 8542 -> 0 bytes .../stdlib/056_GenesisSignerCapability.mv | Bin 454 -> 0 bytes .../compiled/12/11-12/stdlib/057_Oracle.mv | Bin 1982 -> 0 bytes .../12/11-12/stdlib/058_PriceOracle.mv | Bin 825 -> 0 bytes .../12/11-12/stdlib/059_STCUSDOracle.mv | Bin 322 -> 0 bytes .../compiled/12/11-12/stdlib/060_Offer.mv | Bin 538 -> 0 bytes vm/stdlib/compiled/12/11-12/stdlib/061_NFT.mv | Bin 4041 -> 0 bytes .../12/11-12/stdlib/062_LanguageVersion.mv | Bin 143 -> 0 bytes .../12/11-12/stdlib/063_MerkleProof.mv | Bin 369 -> 0 bytes .../11-12/stdlib/064_MerkleNFTDistributor.mv | Bin 1338 -> 0 bytes .../12/11-12/stdlib/065_IdentifierNFT.mv | Bin 1493 -> 0 bytes .../12/11-12/stdlib/066_GenesisNFT.mv | Bin 1242 -> 0 bytes .../11-12/stdlib/067_StdlibUpgradeScripts.mv | Bin 1943 -> 0 bytes .../compiled/12/11-12/stdlib/068_Genesis.mv | Bin 3629 -> 0 bytes .../12/11-12/stdlib/069_GenesisNFTScripts.mv | Bin 125 -> 0 bytes .../11-12/stdlib/070_IdentifierNFTScripts.mv | Bin 204 -> 0 bytes .../12/11-12/stdlib/071_MintDaoProposal.mv | Bin 681 -> 0 bytes .../11-12/stdlib/072_ModuleUpgradeScripts.mv | Bin 901 -> 0 bytes .../12/11-12/stdlib/073_NFTGallery.mv | Bin 2260 -> 0 bytes .../12/11-12/stdlib/074_NFTGalleryScripts.mv | Bin 271 -> 0 bytes .../11-12/stdlib/075_OnChainConfigScripts.mv | Bin 1130 -> 0 bytes .../11-12/stdlib/076_PriceOracleAggregator.mv | Bin 570 -> 0 bytes .../12/11-12/stdlib/077_PriceOracleScripts.mv | Bin 274 -> 0 bytes .../compiled/12/11-12/stdlib/078_Secp256k1.mv | Bin 633 -> 0 bytes .../compiled/12/11-12/stdlib/079_Signature.mv | Bin 430 -> 0 bytes .../stdlib/080_SharedEd25519PublicKey.mv | Bin 615 -> 0 bytes .../compiled/12/11-12/stdlib/081_SimpleMap.mv | Bin 1281 -> 0 bytes .../12/11-12/stdlib/082_StructuredHash.mv | Bin 270 -> 0 bytes .../12/11-12/stdlib/083_StarcoinVerifier.mv | Bin 1988 -> 0 bytes .../compiled/12/11-12/stdlib/084_String.mv | Bin 936 -> 0 bytes .../compiled/12/11-12/stdlib/085_Table.mv | Bin 1107 -> 0 bytes .../12/11-12/stdlib/086_TransactionTimeout.mv | Bin 293 -> 0 bytes .../12/11-12/stdlib/087_TransactionManager.mv | Bin 1307 -> 0 bytes .../12/11-12/stdlib/088_TransferScripts.mv | Bin 777 -> 0 bytes .../12/11-12/stdlib/089_TreasuryScripts.mv | Bin 892 -> 0 bytes .../compiled/12/11-12/stdlib/090_TypeInfo.mv | Bin 341 -> 0 bytes .../compiled/12/11-12/stdlib/091_U256.mv | Bin 1196 -> 0 bytes .../12/11-12/stdlib/092_YieldFarming.mv | Bin 1610 -> 0 bytes .../12/11-12/stdlib/093_YieldFarmingV2.mv | Bin 3429 -> 0 bytes vm/stdlib/compiled/12/stdlib/052_Epoch.mv | Bin 2724 -> 0 bytes vm/stdlib/compiled/12/stdlib/053_EventUtil.mv | Bin 490 -> 0 bytes .../compiled/12/stdlib/054_FixedPoint32.mv | Bin 595 -> 0 bytes .../compiled/12/stdlib/055_GasSchedule.mv | Bin 8542 -> 0 bytes .../12/stdlib/056_GenesisSignerCapability.mv | Bin 454 -> 0 bytes vm/stdlib/compiled/12/stdlib/057_Oracle.mv | Bin 1982 -> 0 bytes .../compiled/12/stdlib/058_PriceOracle.mv | Bin 825 -> 0 bytes .../compiled/12/stdlib/059_STCUSDOracle.mv | Bin 322 -> 0 bytes vm/stdlib/compiled/12/stdlib/060_Offer.mv | Bin 538 -> 0 bytes vm/stdlib/compiled/12/stdlib/061_NFT.mv | Bin 4041 -> 0 bytes .../compiled/12/stdlib/062_LanguageVersion.mv | Bin 143 -> 0 bytes .../compiled/12/stdlib/063_MerkleProof.mv | Bin 369 -> 0 bytes .../12/stdlib/064_MerkleNFTDistributor.mv | Bin 1338 -> 0 bytes .../compiled/12/stdlib/065_IdentifierNFT.mv | Bin 1493 -> 0 bytes .../compiled/12/stdlib/066_GenesisNFT.mv | Bin 1242 -> 0 bytes .../12/stdlib/067_StdlibUpgradeScripts.mv | Bin 1943 -> 0 bytes vm/stdlib/compiled/12/stdlib/068_Genesis.mv | Bin 3629 -> 0 bytes .../12/stdlib/069_GenesisNFTScripts.mv | Bin 125 -> 0 bytes .../12/stdlib/070_IdentifierNFTScripts.mv | Bin 204 -> 0 bytes .../compiled/12/stdlib/071_MintDaoProposal.mv | Bin 681 -> 0 bytes .../12/stdlib/072_ModuleUpgradeScripts.mv | Bin 901 -> 0 bytes .../compiled/12/stdlib/073_NFTGallery.mv | Bin 2260 -> 0 bytes .../12/stdlib/074_NFTGalleryScripts.mv | Bin 271 -> 0 bytes .../12/stdlib/075_OnChainConfigScripts.mv | Bin 1130 -> 0 bytes .../12/stdlib/076_PriceOracleAggregator.mv | Bin 570 -> 0 bytes .../12/stdlib/077_PriceOracleScripts.mv | Bin 274 -> 0 bytes vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv | Bin 633 -> 0 bytes vm/stdlib/compiled/12/stdlib/079_Signature.mv | Bin 430 -> 0 bytes .../12/stdlib/080_SharedEd25519PublicKey.mv | Bin 615 -> 0 bytes vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv | Bin 1281 -> 0 bytes .../compiled/12/stdlib/082_StructuredHash.mv | Bin 270 -> 0 bytes .../12/stdlib/083_StarcoinVerifier.mv | Bin 1988 -> 0 bytes vm/stdlib/compiled/12/stdlib/084_String.mv | Bin 936 -> 0 bytes vm/stdlib/compiled/12/stdlib/085_Table.mv | Bin 1107 -> 0 bytes .../12/stdlib/086_TransactionTimeout.mv | Bin 293 -> 0 bytes .../12/stdlib/087_TransactionManager.mv | Bin 1307 -> 0 bytes .../compiled/12/stdlib/088_TransferScripts.mv | Bin 777 -> 0 bytes .../compiled/12/stdlib/089_TreasuryScripts.mv | Bin 892 -> 0 bytes vm/stdlib/compiled/12/stdlib/090_TypeInfo.mv | Bin 341 -> 0 bytes vm/stdlib/compiled/12/stdlib/091_U256.mv | Bin 1196 -> 0 bytes .../compiled/12/stdlib/092_YieldFarming.mv | Bin 1610 -> 0 bytes .../compiled/12/stdlib/093_YieldFarmingV2.mv | Bin 3429 -> 0 bytes vm/stdlib/compiled/latest/stdlib/052_Epoch.mv | Bin 2724 -> 0 bytes .../compiled/latest/stdlib/053_EventUtil.mv | Bin 490 -> 0 bytes .../latest/stdlib/054_FixedPoint32.mv | Bin 595 -> 0 bytes .../compiled/latest/stdlib/055_GasSchedule.mv | Bin 8542 -> 0 bytes .../stdlib/056_GenesisSignerCapability.mv | Bin 454 -> 0 bytes .../compiled/latest/stdlib/057_Oracle.mv | Bin 1982 -> 0 bytes .../compiled/latest/stdlib/058_PriceOracle.mv | Bin 825 -> 0 bytes .../latest/stdlib/059_STCUSDOracle.mv | Bin 322 -> 0 bytes vm/stdlib/compiled/latest/stdlib/060_Offer.mv | Bin 538 -> 0 bytes vm/stdlib/compiled/latest/stdlib/061_NFT.mv | Bin 4041 -> 0 bytes .../latest/stdlib/062_LanguageVersion.mv | Bin 143 -> 0 bytes .../compiled/latest/stdlib/063_MerkleProof.mv | Bin 369 -> 0 bytes .../latest/stdlib/064_MerkleNFTDistributor.mv | Bin 1338 -> 0 bytes .../latest/stdlib/065_IdentifierNFT.mv | Bin 1493 -> 0 bytes .../compiled/latest/stdlib/066_GenesisNFT.mv | Bin 1242 -> 0 bytes .../latest/stdlib/067_StdlibUpgradeScripts.mv | Bin 1943 -> 0 bytes .../compiled/latest/stdlib/068_Genesis.mv | Bin 3629 -> 0 bytes .../latest/stdlib/069_GenesisNFTScripts.mv | Bin 125 -> 0 bytes .../latest/stdlib/070_IdentifierNFTScripts.mv | Bin 204 -> 0 bytes .../latest/stdlib/071_MintDaoProposal.mv | Bin 681 -> 0 bytes .../latest/stdlib/072_ModuleUpgradeScripts.mv | Bin 901 -> 0 bytes .../compiled/latest/stdlib/073_NFTGallery.mv | Bin 2260 -> 0 bytes .../latest/stdlib/074_NFTGalleryScripts.mv | Bin 271 -> 0 bytes .../latest/stdlib/075_OnChainConfigScripts.mv | Bin 1130 -> 0 bytes .../stdlib/076_PriceOracleAggregator.mv | Bin 570 -> 0 bytes .../latest/stdlib/077_PriceOracleScripts.mv | Bin 274 -> 0 bytes .../compiled/latest/stdlib/078_Secp256k1.mv | Bin 633 -> 0 bytes .../compiled/latest/stdlib/079_Signature.mv | Bin 430 -> 0 bytes .../stdlib/080_SharedEd25519PublicKey.mv | Bin 615 -> 0 bytes .../compiled/latest/stdlib/081_SimpleMap.mv | Bin 1281 -> 0 bytes .../latest/stdlib/082_StructuredHash.mv | Bin 270 -> 0 bytes .../latest/stdlib/083_StarcoinVerifier.mv | Bin 1988 -> 0 bytes .../compiled/latest/stdlib/084_String.mv | Bin 936 -> 0 bytes vm/stdlib/compiled/latest/stdlib/085_Table.mv | Bin 1107 -> 0 bytes .../latest/stdlib/086_TransactionTimeout.mv | Bin 293 -> 0 bytes .../latest/stdlib/087_TransactionManager.mv | Bin 1307 -> 0 bytes .../latest/stdlib/088_TransferScripts.mv | Bin 777 -> 0 bytes .../latest/stdlib/089_TreasuryScripts.mv | Bin 892 -> 0 bytes .../compiled/latest/stdlib/090_TypeInfo.mv | Bin 341 -> 0 bytes vm/stdlib/compiled/latest/stdlib/091_U256.mv | Bin 1196 -> 0 bytes .../latest/stdlib/092_YieldFarming.mv | Bin 1610 -> 0 bytes .../latest/stdlib/093_YieldFarmingV2.mv | Bin 3429 -> 0 bytes 159 files changed, 549 insertions(+), 323 deletions(-) delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/053_EventUtil.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/056_GenesisSignerCapability.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/057_Oracle.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/059_STCUSDOracle.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/061_NFT.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/062_LanguageVersion.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/063_MerkleProof.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/066_GenesisNFT.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/067_StdlibUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/068_Genesis.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/070_IdentifierNFTScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/071_MintDaoProposal.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/075_OnChainConfigScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/076_PriceOracleAggregator.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/082_StructuredHash.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/084_String.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/086_TransactionTimeout.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/090_TypeInfo.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/092_YieldFarming.mv delete mode 100644 vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/052_Epoch.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/053_EventUtil.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/056_GenesisSignerCapability.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/057_Oracle.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/059_STCUSDOracle.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/060_Offer.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/061_NFT.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/062_LanguageVersion.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/063_MerkleProof.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/066_GenesisNFT.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/067_StdlibUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/068_Genesis.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/070_IdentifierNFTScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/071_MintDaoProposal.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/075_OnChainConfigScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/076_PriceOracleAggregator.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/079_Signature.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/082_StructuredHash.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/084_String.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/085_Table.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/086_TransactionTimeout.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/090_TypeInfo.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/091_U256.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/092_YieldFarming.mv delete mode 100644 vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/052_Epoch.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/053_EventUtil.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/056_GenesisSignerCapability.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/057_Oracle.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/059_STCUSDOracle.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/060_Offer.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/061_NFT.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/062_LanguageVersion.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/063_MerkleProof.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/066_GenesisNFT.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/067_StdlibUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/068_Genesis.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/070_IdentifierNFTScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/071_MintDaoProposal.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/075_OnChainConfigScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/076_PriceOracleAggregator.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/079_Signature.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/082_StructuredHash.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/084_String.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/085_Table.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/086_TransactionTimeout.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/090_TypeInfo.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/091_U256.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/092_YieldFarming.mv delete mode 100644 vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv diff --git a/Cargo.lock b/Cargo.lock index 003d2bd61e..1afc771ef7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9312,6 +9312,7 @@ dependencies = [ "rand_core 0.6.4", "serde 1.0.152", "starcoin-accumulator", + "starcoin-config", "starcoin-crypto", "starcoin-network-rpc-api", "starcoin-service-registry", diff --git a/benchmarks/src/chain.rs b/benchmarks/src/chain.rs index a9af4ec157..c7f9e3344a 100644 --- a/benchmarks/src/chain.rs +++ b/benchmarks/src/chain.rs @@ -46,8 +46,14 @@ impl ChainBencher { Genesis::init_and_check_storage(&net, storage.clone(), temp_path.path()) .expect("init storage by genesis fail."); - let chain = BlockChain::new(net.time_service(), chain_info.head().id(), storage, None) - .expect("create block chain should success."); + let chain = BlockChain::new( + net.time_service(), + chain_info.head().id(), + storage, + net.id().clone(), + None, + ) + .expect("create block chain should success."); let miner_account = AccountInfo::random(); ChainBencher { diff --git a/block-relayer/src/block_relayer.rs b/block-relayer/src/block_relayer.rs index 4a180353b6..168c902c77 100644 --- a/block-relayer/src/block_relayer.rs +++ b/block-relayer/src/block_relayer.rs @@ -78,18 +78,15 @@ impl BlockRelayer { &self, network: NetworkServiceRef, executed_block: Arc, - tips_hash: Option> + tips_hash: Option>, ) { if !self.is_nearly_synced() { debug!("[block-relay] Ignore NewHeadBlock event because the node has not been synchronized yet."); return; } let compact_block = executed_block.block().clone().into(); - let compact_block_msg = CompactBlockMessage::new( - compact_block, - executed_block.block_info.clone(), - tips_hash - ); + let compact_block_msg = + CompactBlockMessage::new(compact_block, executed_block.block_info.clone(), tips_hash); network.broadcast(NotificationMessage::CompactBlock(Box::new( compact_block_msg, ))); @@ -313,7 +310,7 @@ impl EventHandler for BlockRelayer { return; } }; - self.broadcast_compact_block(network, event.0,event.1); + self.broadcast_compact_block(network, event.0, event.1); } } diff --git a/chain/api/Cargo.toml b/chain/api/Cargo.toml index 47235e7bb9..1648fcdee5 100644 --- a/chain/api/Cargo.toml +++ b/chain/api/Cargo.toml @@ -16,6 +16,7 @@ starcoin-types = { workspace = true } starcoin-vm-types = { workspace = true } thiserror = { workspace = true } starcoin-network-rpc-api = { workspace = true } +starcoin-config = { workspace = true } [dev-dependencies] diff --git a/chain/api/src/chain.rs b/chain/api/src/chain.rs index 153de45a90..4f25192e6b 100644 --- a/chain/api/src/chain.rs +++ b/chain/api/src/chain.rs @@ -3,6 +3,7 @@ use anyhow::Result; use starcoin_accumulator::accumulator_info::AccumulatorInfo; +use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; use starcoin_state_api::ChainStateReader; use starcoin_statedb::ChainStateDB; @@ -108,6 +109,7 @@ pub trait ChainReader { /// get the current tips hash value fn current_tips_hash(&self) -> Option; + fn net_id(&self) -> ChainNetworkID; } pub trait ChainWriter { @@ -116,7 +118,11 @@ pub trait ChainWriter { fn connect(&mut self, executed_block: ExecutedBlock) -> Result; /// Verify, Execute and Connect block to current chain. - fn apply(&mut self, block: Block,parents_hash:Option>) -> Result; + fn apply( + &mut self, + block: Block, + parents_hash: Option>, + ) -> Result; fn chain_state(&mut self) -> &ChainStateDB; diff --git a/chain/mock/src/mock_chain.rs b/chain/mock/src/mock_chain.rs index 46c6135c78..6259bb4a3b 100644 --- a/chain/mock/src/mock_chain.rs +++ b/chain/mock/src/mock_chain.rs @@ -25,7 +25,13 @@ impl MockChain { let (storage, chain_info, _) = Genesis::init_storage_for_test(&net).expect("init storage by genesis fail."); - let chain = BlockChain::new(net.time_service(), chain_info.head().id(), storage, None)?; + let chain = BlockChain::new( + net.time_service(), + chain_info.head().id(), + storage, + net.id().clone(), + None, + )?; let miner = AccountInfo::random(); Ok(Self::new_inner(net, chain, miner)) } @@ -36,7 +42,13 @@ impl MockChain { head_block_hash: HashValue, miner: AccountInfo, ) -> Result { - let chain = BlockChain::new(net.time_service(), head_block_hash, storage, None)?; + let chain = BlockChain::new( + net.time_service(), + head_block_hash, + storage, + net.id().clone(), + None, + )?; Ok(Self::new_inner(net, chain, miner)) } @@ -71,6 +83,7 @@ impl MockChain { self.head.time_service(), block_id, self.head.get_storage(), + self.net.id().clone(), None, ) } @@ -92,6 +105,7 @@ impl MockChain { self.net.time_service(), new_block_id, self.head.get_storage(), + self.net.id().clone(), None, )?; let branch_total_difficulty = branch.get_total_difficulty()?; diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index 36f48984cb..d00b0ba5bb 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -92,10 +92,7 @@ impl EventHandler for ChainReaderService { fn handle_event(&mut self, event: NewHeadBlock, _ctx: &mut ServiceContext) { let new_head = event.0.block().header(); if let Err(e) = if self.inner.get_main().can_connect(event.0.as_ref()) { - match self - .inner - .update_chain_head(event.0.as_ref().clone()) - { + match self.inner.update_chain_head(event.0.as_ref().clone()) { Ok(_) => self.inner.update_dag_accumulator(new_head.id()), Err(e) => Err(e), } @@ -302,6 +299,7 @@ impl ChainReaderServiceInner { net.time_service(), startup_info.main, storage.clone(), + config.net().id().clone(), vm_metrics.clone(), )?; Ok(Self { @@ -318,10 +316,7 @@ impl ChainReaderServiceInner { &self.main } - pub fn update_chain_head( - &mut self, - block: ExecutedBlock, - ) -> Result<()> { + pub fn update_chain_head(&mut self, block: ExecutedBlock) -> Result<()> { self.main.connect(block)?; Ok(()) } @@ -332,6 +327,7 @@ impl ChainReaderServiceInner { net.time_service(), new_head_id, self.storage.clone(), + self.config.net().id().clone(), self.vm_metrics.clone(), )?; Ok(()) diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 814d81f799..43247700d1 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -13,6 +13,7 @@ use starcoin_chain_api::{ verify_block, ChainReader, ChainWriter, ConnectBlockError, EventWithProof, ExcludedTxns, ExecutedBlock, MintedUncleNumber, TransactionInfoWithProof, VerifiedBlock, VerifyBlockField, }; +use starcoin_config::ChainNetworkID; use starcoin_consensus::dag::types::ghostdata::GhostdagData; use starcoin_consensus::{BlockDAG, Consensus, FlexiDagStorage}; use starcoin_crypto::hash::PlainCryptoHash; @@ -66,6 +67,7 @@ pub struct BlockChain { epoch: Epoch, vm_metrics: Option, dag_accumulator: Option, + net: ChainNetworkID, } impl BlockChain { @@ -73,12 +75,35 @@ impl BlockChain { time_service: Arc, head_block_hash: HashValue, storage: Arc, + net: ChainNetworkID, vm_metrics: Option, ) -> Result { let head = storage .get_block_by_hash(head_block_hash)? .ok_or_else(|| format_err!("Can not find block by hash {:?}", head_block_hash))?; - Self::new_with_uncles(time_service, head, None, storage, vm_metrics) + Self::new_with_uncles(time_service, head, None, storage, net, vm_metrics) + } + + fn get_dag_data( + storage: Arc, + header: &BlockHeader, + net: ChainNetworkID, + ) -> Result<(Option, Option>)> { + let (op_dag_accumulator_info, op_tips) = storage.get_flexidag_init_data(header, net)?; + match (op_dag_accumulator_info, op_tips.clone()) { + (Some(dag_accumulator_info), Some(_tips)) => { + let dag_accumulator = Some(info_2_accumulator( + dag_accumulator_info, + AccumulatorStoreType::SyncDag, + storage.as_ref(), + )); + Ok((dag_accumulator, op_tips)) + } + (None, None) => Ok((None, None)), + _ => { + bail!("dag accumulator info and tips should be both None or Some") + } + } } fn new_with_uncles( @@ -86,6 +111,7 @@ impl BlockChain { head_block: Block, uncles: Option>, storage: Arc, + net: ChainNetworkID, vm_metrics: Option, ) -> Result { let block_info = storage @@ -102,18 +128,21 @@ impl BlockChain { .ok_or_else(|| format_err!("Can not find genesis hash in storage."))?; let head_id = head_block.id(); watch(CHAIN_WATCH_NAME, "n1253"); - let dag_accumulator = match storage.get_dag_accumulator_info(head_id)? { - Some(accmulator_info) => Some(info_2_accumulator( - accmulator_info, - AccumulatorStoreType::SyncDag, - storage.as_ref(), - )), - None => None, - }; - let dag_snapshot_tips = storage - .get_accumulator_snapshot_storage() - .get(head_id)? - .map(|snapshot| snapshot.child_hashes); + + // let dag_accumulator = match storage.get_dag_accumulator_info(head_id)? { + // Some(accmulator_info) => Some(info_2_accumulator( + // accmulator_info, + // AccumulatorStoreType::SyncDag, + // storage.as_ref(), + // )), + // None => None, + // }; + // let dag_snapshot_tips = storage + // .get_accumulator_snapshot_storage() + // .get(head_id)? + // .map(|snapshot| snapshot.child_hashes); + let (dag_accumulator, dag_snapshot_tips) = + Self::get_dag_data(storage.clone(), head_block.header(), net.clone())?; let mut chain = Self { genesis_hash: genesis, time_service, @@ -137,6 +166,7 @@ impl BlockChain { epoch, vm_metrics, dag_accumulator, + net, }; watch(CHAIN_WATCH_NAME, "n1251"); match uncles { @@ -152,6 +182,7 @@ impl BlockChain { storage: Arc, genesis_epoch: Epoch, genesis_block: Block, + net: ChainNetworkID, ) -> Result { debug_assert!(genesis_block.header().is_genesis()); let txn_accumulator = MerkleAccumulator::new_empty( @@ -186,7 +217,7 @@ impl BlockChain { new_tips, dag_accumulator.get_info(), )?; - Self::new(time_service, executed_block.block.id(), storage, None) + Self::new(time_service, executed_block.block.id(), storage, net, None) } pub fn current_epoch_uncles_size(&self) -> u64 { @@ -370,7 +401,11 @@ impl BlockChain { V::verify_block(self, block) } - pub fn apply_with_verifier(&mut self, block: Block, parents_hash: Option>) -> Result + pub fn apply_with_verifier( + &mut self, + block: Block, + parents_hash: Option>, + ) -> Result where V: BlockVerifier, { @@ -391,13 +426,11 @@ impl BlockChain { .storage .get_block_info(block.id())? .ok_or_else(|| format_err!("Can not find block info by hash {:?}", block.id()))?; - self.connect( - ExecutedBlock { - block, - block_info, - parents_hash, - } - ) + self.connect(ExecutedBlock { + block, + block_info, + parents_hash, + }) } //TODO consider move this logic to BlockExecutor @@ -410,8 +443,7 @@ impl BlockChain { parent_status: Option, block: Block, vm_metrics: Option, - parents_hash: Option> - + parents_hash: Option>, ) -> Result { let header = block.header(); debug_assert!(header.is_genesis() || parent_status.is_some()); @@ -703,6 +735,10 @@ impl ChainReader for BlockChain { } } + fn net_id(&self) -> ChainNetworkID { + self.net.clone() + } + fn get_header(&self, hash: HashValue) -> Result> { self.storage .get_block_header_by_hash(hash) @@ -878,6 +914,7 @@ impl ChainReader for BlockChain { head, uncles, self.storage.clone(), + self.net.clone(), self.vm_metrics.clone(), //TODO: check missing blocks need to be clean ) @@ -914,7 +951,11 @@ impl ChainReader for BlockChain { FullVerifier::verify_block(self, block) } - fn execute(&self, verified_block: VerifiedBlock, parents_hash: Option>) -> Result { + fn execute( + &self, + verified_block: VerifiedBlock, + parents_hash: Option>, + ) -> Result { Self::execute_block_and_save( self.storage.as_ref(), self.statedb.fork(), @@ -924,7 +965,7 @@ impl ChainReader for BlockChain { Some(self.status.status.clone()), verified_block.0, self.vm_metrics.clone(), - parents_hash + parents_hash, ) } @@ -1201,7 +1242,11 @@ impl ChainWriter for BlockChain { Ok(executed_block) } - fn apply(&mut self, block: Block, parents_hash: Option>) -> Result { + fn apply( + &mut self, + block: Block, + parents_hash: Option>, + ) -> Result { self.apply_with_verifier::(block, parents_hash) } diff --git a/cmd/db-exporter/src/main.rs b/cmd/db-exporter/src/main.rs index 4e5f7a44fd..4a7635c651 100644 --- a/cmd/db-exporter/src/main.rs +++ b/cmd/db-exporter/src/main.rs @@ -634,8 +634,14 @@ pub fn export_block_range( ))?); let (chain_info, _) = Genesis::init_and_check_storage(&net, storage.clone(), from_dir.as_ref())?; - let chain = BlockChain::new(net.time_service(), chain_info.head().id(), storage, None) - .expect("create block chain should success."); + let chain = BlockChain::new( + net.time_service(), + chain_info.head().id(), + storage, + net.id().clone(), + None, + ) + .expect("create block chain should success."); let cur_num = chain.status().head().number(); let end = if cur_num > end + BLOCK_GAP { end @@ -716,6 +722,7 @@ pub fn apply_block( net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -757,12 +764,8 @@ pub fn apply_block( let block_hash = block.header().id(); let block_number = block.header().number(); match verifier { - Verifier::Basic => { - chain.apply_with_verifier::(block, None)? - } - Verifier::Consensus => { - chain.apply_with_verifier::(block, None)? - } + Verifier::Basic => chain.apply_with_verifier::(block, None)?, + Verifier::Consensus => chain.apply_with_verifier::(block, None)?, Verifier::Full => chain.apply_with_verifier::(block, None)?, Verifier::None => chain.apply_with_verifier::(block, None)?, }; @@ -776,7 +779,7 @@ pub fn apply_block( let use_time = SystemTime::now().duration_since(start_time)?; println!("apply block use time: {:?}", use_time.as_secs()); let chain_info = storage - .get_chain_info()? + .get_chain_info(net.id().clone())? .ok_or_else(|| format_err!("{}", "get chain_info error"))?; println!("chain_info {}", chain_info); Ok(()) @@ -798,6 +801,7 @@ pub fn startup_info_back( net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -843,6 +847,7 @@ pub fn gen_block_transactions( net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -944,7 +949,7 @@ pub fn execute_transaction_with_create_account( println!("trans {}", block.transactions().len()); } let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, )?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -967,7 +972,7 @@ pub fn execute_transaction_with_miner_create_account( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None,)?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -996,7 +1001,7 @@ pub fn execute_transaction_with_miner_create_account( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, )?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1019,7 +1024,7 @@ pub fn execute_empty_transaction_with_miner( let block = ConsensusStrategy::Dummy.create_block(block_template, net.time_service().as_ref())?; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, )?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; for _i in 0..block_num { @@ -1046,7 +1051,7 @@ pub fn execute_empty_transaction_with_miner( } send_sequence += block.transactions().len() as u64; let block_hash = block.header.id(); - chain.apply_with_verifier::(block, None, )?; + chain.apply_with_verifier::(block, None)?; let startup_info = StartupInfo::new(block_hash); storage.save_startup_info(startup_info)?; @@ -1299,6 +1304,7 @@ pub fn export_snapshot( net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -1317,8 +1323,14 @@ pub fn export_snapshot( let cur_block = chain .get_block_by_number(cur_num)? .ok_or_else(|| format_err!("get block by number {} error", cur_num))?; - let chain = BlockChain::new(net.time_service(), cur_block.id(), storage.clone(), None) - .expect("create block chain should success."); + let chain = BlockChain::new( + net.time_service(), + cur_block.id(), + storage.clone(), + net.id().clone(), + None, + ) + .expect("create block chain should success."); let cur_num = chain.epoch().start_block_number(); @@ -1640,6 +1652,7 @@ pub fn apply_snapshot( net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."), @@ -1973,6 +1986,7 @@ pub fn gen_turbo_stm_transactions(to_dir: PathBuf, block_num: Option) -> an net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -1999,6 +2013,7 @@ pub fn apply_turbo_stm_block( net.time_service(), chain_info_seq.head().id(), storage_seq.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); @@ -2057,6 +2072,7 @@ pub fn apply_turbo_stm_block( net.time_service(), chain_info_stm.head().id(), storage_stm.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); diff --git a/cmd/replay/src/main.rs b/cmd/replay/src/main.rs index 9913095d62..7a06f5ff0e 100644 --- a/cmd/replay/src/main.rs +++ b/cmd/replay/src/main.rs @@ -80,8 +80,14 @@ fn main() -> anyhow::Result<()> { ); let (chain_info, _) = Genesis::init_and_check_storage(&net, storage.clone(), from_dir.as_ref()) .expect("init storage by genesis fail."); - let chain = BlockChain::new(net.time_service(), chain_info.head().id(), storage, None) - .expect("create block chain should success."); + let chain = BlockChain::new( + net.time_service(), + chain_info.head().id(), + storage, + net.id().clone(), + None, + ) + .expect("create block chain should success."); let storage2 = Arc::new( Storage::new(StorageInstance::new_cache_and_db_instance( @@ -97,6 +103,7 @@ fn main() -> anyhow::Result<()> { net.time_service(), chain_info2.status().head().id(), storage2.clone(), + net.id().clone(), None, ) .expect("create block chain should success."); diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index f16dc6b0ed..4683b286d8 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -261,11 +261,12 @@ impl Genesis { storage.clone(), net.genesis_epoch(), self.block.clone(), + net.id().clone(), )?; let startup_info = StartupInfo::new(genesis_chain.current_header().id()); storage.save_startup_info(startup_info)?; storage - .get_chain_info()? + .get_chain_info(net.id().clone())? .ok_or_else(|| format_err!("ChainInfo should exist after genesis block executed.")) } @@ -318,7 +319,7 @@ impl Genesis { data_dir: &Path, ) -> Result<(ChainInfo, Genesis)> { debug!("load startup_info."); - let (chain_info, genesis) = match storage.get_chain_info() { + let (chain_info, genesis) = match storage.get_chain_info(net.id().clone()) { Ok(Some(chain_info)) => { debug!("Get chain info {:?} from db", chain_info); info!("Check genesis file."); diff --git a/miner/src/create_block_template/mod.rs b/miner/src/create_block_template/mod.rs index 09f260b414..f18c1a7a4e 100644 --- a/miner/src/create_block_template/mod.rs +++ b/miner/src/create_block_template/mod.rs @@ -111,10 +111,7 @@ impl ActorService for BlockBuilderService { impl EventHandler for BlockBuilderService { fn handle_event(&mut self, msg: NewHeadBlock, _ctx: &mut ServiceContext) { - if let Err(e) = self - .inner - .update_chain(msg.0.as_ref().clone()) - { + if let Err(e) = self.inner.update_chain(msg.0.as_ref().clone()) { error!("err : {:?}", e) } } @@ -214,6 +211,7 @@ where net.time_service(), block_id, storage.clone(), + net.id().clone(), vm_metrics.clone(), )?; @@ -243,10 +241,7 @@ where } } - pub fn update_chain( - &mut self, - block: ExecutedBlock, - ) -> Result<()> { + pub fn update_chain(&mut self, block: ExecutedBlock) -> Result<()> { let current_header = self.chain.current_header(); let current_id = current_header.id(); if self.chain.can_connect(&block) { @@ -256,6 +251,7 @@ where self.chain.time_service(), block.header().id(), self.storage.clone(), + self.chain.net_id(), self.vm_metrics.clone(), )?; //current block possible be uncle. @@ -321,7 +317,7 @@ where let tips_header = match self.chain.status().tips_hash { Some(_) => self.chain.status().tips_hash, - None => bail!("currently, the chain must be dag, so the tips hash should not be None"), + None => None, }; let uncles = self.find_uncles(); diff --git a/miner/src/create_block_template/test_create_block_template.rs b/miner/src/create_block_template/test_create_block_template.rs index ebcb912977..eeb610cbde 100644 --- a/miner/src/create_block_template/test_create_block_template.rs +++ b/miner/src/create_block_template/test_create_block_template.rs @@ -79,7 +79,14 @@ fn test_switch_main() { let net = node_config.net(); for i in 0..times { - let mut main = BlockChain::new(net.time_service(), head_id, storage.clone(), None).unwrap(); + let mut main = BlockChain::new( + net.time_service(), + head_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let mut tmp_inner = Inner::new( net, @@ -116,8 +123,14 @@ fn test_switch_main() { } for i in 0..3 { - let mut new_main = - BlockChain::new(net.time_service(), head_id, storage.clone(), None).unwrap(); + let mut new_main = BlockChain::new( + net.time_service(), + head_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let block_template = if i == 0 { let tmp = Inner::new( @@ -196,7 +209,14 @@ fn test_do_uncles() { let net = node_config.net(); for _i in 0..times { - let mut main = BlockChain::new(net.time_service(), head_id, storage.clone(), None).unwrap(); + let mut main = BlockChain::new( + net.time_service(), + head_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let mut tmp_inner = Inner::new( net, @@ -224,8 +244,14 @@ fn test_do_uncles() { // branch for _i in 0..times { - let mut branch = - BlockChain::new(net.time_service(), genesis_id, storage.clone(), None).unwrap(); + let mut branch = BlockChain::new( + net.time_service(), + genesis_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let inner = Inner::new( net, storage.clone(), @@ -254,7 +280,14 @@ fn test_do_uncles() { // uncles for i in 0..times { - let mut main = BlockChain::new(net.time_service(), head_id, storage.clone(), None).unwrap(); + let mut main = BlockChain::new( + net.time_service(), + head_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let block_template = main_inner .as_ref() @@ -367,8 +400,14 @@ fn test_new_branch() { let mut new_head_id = genesis_id; let net = node_config.net(); for i in 0..(times * 2) { - let mut branch = - BlockChain::new(net.time_service(), new_head_id, storage.clone(), None).unwrap(); + let mut branch = BlockChain::new( + net.time_service(), + new_head_id, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let inner = Inner::new( net, storage.clone(), diff --git a/network-rpc/src/rpc.rs b/network-rpc/src/rpc.rs index 1d6ca41e78..f51a1dc261 100644 --- a/network-rpc/src/rpc.rs +++ b/network-rpc/src/rpc.rs @@ -10,10 +10,11 @@ use starcoin_accumulator::AccumulatorNode; use starcoin_chain_service::{ChainAsyncService, ChainReaderService}; use starcoin_crypto::HashValue; use starcoin_network_rpc_api::{ - dag_protocol, gen_server, BlockBody, GetAccountState, GetAccumulatorNodeByNodeHash, - GetBlockHeadersByNumber, GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, - GetTableInfo, GetTxnsWithHash, GetTxnsWithSize, Ping, RpcRequest, MAX_BLOCK_HEADER_REQUEST_SIZE, - MAX_BLOCK_INFO_REQUEST_SIZE, MAX_BLOCK_REQUEST_SIZE, MAX_TXN_REQUEST_SIZE, + dag_protocol, gen_server, BlockBody, GetAccountState, GetAccumulatorNodeByNodeHash, + GetBlockHeadersByNumber, GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, + GetTableInfo, GetTxnsWithHash, GetTxnsWithSize, Ping, RpcRequest, + MAX_BLOCK_HEADER_REQUEST_SIZE, MAX_BLOCK_INFO_REQUEST_SIZE, MAX_BLOCK_REQUEST_SIZE, + MAX_TXN_REQUEST_SIZE, }; use starcoin_service_registry::ServiceRef; use starcoin_state_api::{ChainStateAsyncService, StateWithProof, StateWithTableItemProof}; diff --git a/network/api/src/messages.rs b/network/api/src/messages.rs index e6c806aa48..053fa3a5c5 100644 --- a/network/api/src/messages.rs +++ b/network/api/src/messages.rs @@ -48,30 +48,26 @@ impl Sample for TransactionsMessage { pub struct CompactBlockMessage { pub compact_block: CompactBlock, pub block_info: BlockInfo, - pub tips_hash: Option> + pub tips_hash: Option>, } impl CompactBlockMessage { pub fn new( compact_block: CompactBlock, block_info: BlockInfo, - tips_hash: Option> + tips_hash: Option>, ) -> Self { Self { compact_block, block_info, - tips_hash + tips_hash, } } } impl Sample for CompactBlockMessage { fn sample() -> Self { - Self::new( - CompactBlock::sample(), - BlockInfo::sample(), - None - ) + Self::new(CompactBlock::sample(), BlockInfo::sample(), None) } } diff --git a/node/src/network_service_factory.rs b/node/src/network_service_factory.rs index 173ee9295b..aa381d5d11 100644 --- a/node/src/network_service_factory.rs +++ b/node/src/network_service_factory.rs @@ -28,7 +28,7 @@ impl ServiceFactory for NetworkServiceFactory { NodePeerMessageHandler::new(txpool_service, block_relayer, announcement_service); let chain_info = storage - .get_chain_info()? + .get_chain_info(config.net().id().clone())? .ok_or_else(|| format_err!("Can not get chain info."))?; let actor_service = NetworkActorService::new( config, diff --git a/node/src/node.rs b/node/src/node.rs index 99a03b93a9..bb75b58903 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -138,7 +138,9 @@ impl ServiceHandler for NodeService { .start_service_sync(GenerateBlockEventPacemaker::service_name()), ), NodeRequest::ResetNode(block_hash) => { - let connect_service = ctx.service_ref::>()?.clone(); + let connect_service = ctx + .service_ref::>()? + .clone(); let dag = ctx .get_shared::>() .expect("ghost dag object does not exits"); @@ -371,12 +373,9 @@ impl NodeService { let flex_dag_db = FlexiDagStorage::create_from_path("./smolstc", flex_dag_config) .expect("Failed to create flexidag storage"); - let mut dag = BlockDAG::new( - genesis.block().id(), - 8, - flex_dag_db, - ); - dag.init_with_genesis(DagHeader::new_genesis(genesis.block().header().clone())).expect("dag init with genesis"); + let mut dag = BlockDAG::new(genesis.block().id(), 8, flex_dag_db); + dag.init_with_genesis(DagHeader::new_genesis(genesis.block().header().clone())) + .expect("dag init with genesis"); registry.put_shared(Arc::new(Mutex::new(dag))).await?; info!( diff --git a/rpc/server/src/module/pubsub/tests.rs b/rpc/server/src/module/pubsub/tests.rs index ae2b966064..587b8eaa43 100644 --- a/rpc/server/src/module/pubsub/tests.rs +++ b/rpc/server/src/module/pubsub/tests.rs @@ -38,7 +38,13 @@ pub async fn test_subscribe_to_events() -> Result<()> { test_helper::start_txpool_with_miner(1000, true).await; let startup_info = storage.get_startup_info()?.unwrap(); let net = config.net(); - let mut block_chain = BlockChain::new(net.time_service(), startup_info.main, storage, None)?; + let mut block_chain = BlockChain::new( + net.time_service(), + startup_info.main, + storage, + net.id().clone(), + None, + )?; let miner_account = AccountInfo::random(); let pri_key = Ed25519PrivateKey::genesis(); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index aabb151477..4712cf5da9 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -23,6 +23,7 @@ use once_cell::sync::Lazy; use starcoin_accumulator::{ accumulator_info::AccumulatorInfo, node::AccumulatorStoreType, AccumulatorTreeStore, }; +use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; use starcoin_logger::prelude::info; use starcoin_state_store_api::{StateNode, StateNodeStore}; @@ -33,13 +34,21 @@ use starcoin_types::{ startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo}, transaction::{RichTransactionInfo, Transaction}, }; -use starcoin_vm_types::{state_store::table::{TableInfo, TableHandle}, account_address::AccountAddress}; -use table_info::{TableInfoStore, TableInfoStorage}; +use starcoin_vm_types::{ + account_address::AccountAddress, + dag_block_metadata, + state_store::table::{TableHandle, TableInfo}, +}; use std::{ collections::BTreeMap, fmt::{Debug, Display, Formatter}, sync::Arc, }; +use table_info::{TableInfoStorage, TableInfoStore}; +use upgrade::{ + BARNARD_FLEXIDAG_FORK_HEIGHT, DEV_FLEXIDAG_FORK_HEIGHT, HALLEY_FLEXIDAG_FORK_HEIGHT, + MAIN_FLEXIDAG_FORK_HEIGHT, PROXIMA_FLEXIDAG_FORK_HEIGHT, TEST_FLEXIDAG_FORK_HEIGHT, +}; pub use upgrade::{BARNARD_HARD_FORK_HASH, BARNARD_HARD_FORK_HEIGHT}; pub mod accumulator; @@ -173,7 +182,7 @@ static VEC_PREFIX_NAME_V4: Lazy> = Lazy::new(|| { FAILED_BLOCK_PREFIX_NAME, SYNC_FLEXI_DAG_ACCUMULATOR_PREFIX_NAME, SYNC_FLEXI_DAG_SNAPSHOT_PREFIX_NAME, - // TABLE_INFO_PREFIX_NAME, + TABLE_INFO_PREFIX_NAME, ] }); @@ -216,7 +225,7 @@ pub trait BlockStore { fn save_genesis(&self, genesis_hash: HashValue) -> Result<()>; - fn get_chain_info(&self) -> Result>; + fn get_chain_info(&self, id: ChainNetworkID) -> Result>; fn get_block(&self, block_id: HashValue) -> Result>; @@ -323,6 +332,11 @@ pub trait SyncFlexiDagStore { ) -> Result<()>; fn get_dag_accumulator_info(&self, block_id: HashValue) -> Result>; fn get_tips_by_block_id(&self, block_id: HashValue) -> Result>; + fn get_flexidag_init_data( + &self, + head_block: &BlockHeader, + id: ChainNetworkID, + ) -> Result<(Option, Option>)>; } // TODO: remove Arc, we can clone Storage directly. @@ -425,7 +439,7 @@ impl BlockStore for Storage { self.chain_info_storage.save_genesis(genesis_hash) } - fn get_chain_info(&self) -> Result> { + fn get_chain_info(&self, id: ChainNetworkID) -> Result> { let genesis_hash = match self.get_genesis()? { Some(genesis_hash) => genesis_hash, None => return Ok(None), @@ -441,18 +455,14 @@ impl BlockStore for Storage { format_err!("Startup block info {:?} should exist", startup_info.main) })?; - let flexi_dag_accumulator_info = self - .get_dag_accumulator_info(head_block.id()) - .unwrap_or(Some(AccumulatorInfo::default())) - .unwrap(); - let tips_header_hash = self - .get_tips_by_block_id(head_block.id()) - .unwrap_or(vec![genesis_hash]); + let (flexi_dag_accumulator_info, tips_header_hash) = + self.get_flexidag_init_data(&head_block, id)?; + let chain_info = ChainInfo::new( head_block.chain_id(), genesis_hash, - ChainStatus::new(head_block.clone(), head_block_info, Some(tips_header_hash)), - Some(flexi_dag_accumulator_info), + ChainStatus::new(head_block.clone(), head_block_info, tips_header_hash), + flexi_dag_accumulator_info, ); Ok(Some(chain_info)) } @@ -720,6 +730,45 @@ impl SyncFlexiDagStore for Storage { } } } + + fn get_flexidag_init_data( + &self, + head_block: &BlockHeader, + id: ChainNetworkID, + ) -> Result<(Option, Option>)> { + let flexi_dag_number = match id { + ChainNetworkID::Builtin(network_id) => match network_id { + starcoin_config::BuiltinNetworkID::Test => TEST_FLEXIDAG_FORK_HEIGHT, + starcoin_config::BuiltinNetworkID::Dev => DEV_FLEXIDAG_FORK_HEIGHT, + starcoin_config::BuiltinNetworkID::Halley => HALLEY_FLEXIDAG_FORK_HEIGHT, + starcoin_config::BuiltinNetworkID::Proxima => PROXIMA_FLEXIDAG_FORK_HEIGHT, + starcoin_config::BuiltinNetworkID::Barnard => BARNARD_FLEXIDAG_FORK_HEIGHT, + starcoin_config::BuiltinNetworkID::Main => MAIN_FLEXIDAG_FORK_HEIGHT, + }, + ChainNetworkID::Custom(_) => DEV_FLEXIDAG_FORK_HEIGHT, + }; + + let (dag_accumulator_info, tips) = if flexi_dag_number == head_block.number() { + ( + Some(AccumulatorInfo::default()), + Some(vec![head_block.id()]), + ) + } else if flexi_dag_number < head_block.number() { + let dag_accumulator_info = self + .get_dag_accumulator_info(head_block.id())? + .expect("the dag accumulator info must exist!"); + let tips = self.get_tips_by_block_id(head_block.id())?; + assert!( + tips.len() > 0, + "the length of the tips must be greater than 0" + ); + (Some(dag_accumulator_info), Some(tips)) + } else { + (None, None) + }; + + Ok((dag_accumulator_info, tips)) + } } /// Chain storage define diff --git a/storage/src/upgrade.rs b/storage/src/upgrade.rs index da6b9e15d6..b29c1d2545 100644 --- a/storage/src/upgrade.rs +++ b/storage/src/upgrade.rs @@ -29,6 +29,12 @@ pub static BARNARD_HARD_FORK_HASH: Lazy = Lazy::new(|| { ) .expect("") }); +pub static DEV_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; +pub static TEST_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; +pub static PROXIMA_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; +pub static HALLEY_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; +pub static BARNARD_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; +pub static MAIN_FLEXIDAG_FORK_HEIGHT: BlockNumber = 4; impl DBUpgrade { pub fn check_upgrade(instance: &mut StorageInstance) -> Result<()> { diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index 3a1e1f8216..ae776b9cfa 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -379,8 +379,7 @@ where msg: ExecuteRequest, _ctx: &mut ServiceContext>, ) -> Result { - self.chain_service - .execute(msg.block, msg.dag_block_parent) + self.chain_service.execute(msg.block, msg.dag_block_parent) } } diff --git a/sync/src/block_connector/test_illegal_block.rs b/sync/src/block_connector/test_illegal_block.rs index 2afd8a437a..5bf7e28542 100644 --- a/sync/src/block_connector/test_illegal_block.rs +++ b/sync/src/block_connector/test_illegal_block.rs @@ -50,7 +50,8 @@ async fn new_block_and_main() -> (Block, BlockChain) { .get_main() .current_header() .id(); - let main = BlockChain::new(net.time_service(), head_id, storage, None).unwrap(); + let main = + BlockChain::new(net.time_service(), head_id, storage, net.id().clone(), None).unwrap(); let new_block = new_block( None, &mut writeable_block_chain_service, @@ -87,7 +88,14 @@ async fn uncle_block_and_writeable_block_chain( .unwrap() .id(); - let new_branch = BlockChain::new(net.time_service(), tmp_head, storage.clone(), None).unwrap(); + let new_branch = BlockChain::new( + net.time_service(), + tmp_head, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); let (block_template, _) = new_branch .create_block_template(*miner_account.address(), None, Vec::new(), vec![], None) .unwrap(); @@ -122,7 +130,7 @@ fn apply_with_illegal_uncle( .get_main() .current_header() .id(); - let mut main = BlockChain::new(net.time_service(), head_id, storage, None)?; + let mut main = BlockChain::new(net.time_service(), head_id, storage, net.id().clone(), None)?; main.apply(new_block.clone(), None)?; Ok(new_block) } @@ -360,8 +368,14 @@ async fn test_verify_can_not_be_uncle_check_ancestor_failed() { .unwrap() .unwrap() .id(); - let mut new_branch = - BlockChain::new(net.time_service(), tmp_head, storage.clone(), None).unwrap(); + let mut new_branch = BlockChain::new( + net.time_service(), + tmp_head, + storage.clone(), + net.id().clone(), + None, + ) + .unwrap(); for _i in 0..2 { let (block_template, _) = new_branch diff --git a/sync/src/block_connector/test_write_block_chain.rs b/sync/src/block_connector/test_write_block_chain.rs index 95da34b774..2c4822d492 100644 --- a/sync/src/block_connector/test_write_block_chain.rs +++ b/sync/src/block_connector/test_write_block_chain.rs @@ -151,6 +151,7 @@ fn gen_fork_block_chain( net.time_service(), parent_id, writeable_block_chain_service.get_main().get_storage(), + net.id().clone(), None, ) .unwrap(); diff --git a/sync/src/block_connector/test_write_dag_block_chain.rs b/sync/src/block_connector/test_write_dag_block_chain.rs index c74c9aef83..175bb0a5ce 100644 --- a/sync/src/block_connector/test_write_dag_block_chain.rs +++ b/sync/src/block_connector/test_write_dag_block_chain.rs @@ -57,7 +57,8 @@ pub fn gen_dag_blocks( | super::write_block_chain::ConnectOk::Connect(block) => Some(block.header().id()), super::write_block_chain::ConnectOk::DagConnected | super::write_block_chain::ConnectOk::MainDuplicate - | super::write_block_chain::ConnectOk::DagPending => { + | super::write_block_chain::ConnectOk::DagPending + | super::write_block_chain::ConnectOk::DagConnectMissingBlock => { unreachable!("should not reach here, result: {:?}", result); } } @@ -122,6 +123,7 @@ fn gen_fork_dag_block_chain( net.time_service(), parent_id, writeable_block_chain_service.get_main().get_storage(), + net.id().clone(), None, ) .unwrap(); diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 40d58a51fb..fa28c36de1 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -75,7 +75,10 @@ impl ConnectOk { ConnectOk::ExeConnectMain(block) => Some(block.clone()), ConnectOk::ExeConnectBranch(block) => Some(block.clone()), ConnectOk::Connect(block) => Some(block.clone()), - ConnectOk::DagConnected | ConnectOk::MainDuplicate | ConnectOk::DagPending | ConnectOk::DagConnectMissingBlock => None, + ConnectOk::DagConnected + | ConnectOk::MainDuplicate + | ConnectOk::DagPending + | ConnectOk::DagConnectMissingBlock => None, } } } @@ -107,7 +110,10 @@ where .map(|metrics| metrics.chain_block_connect_time.start_timer()); let result = if parents_hash.is_some() { - self.connect_dag_inner(block, parents_hash.expect("parents_hash should not be None")) + self.connect_dag_inner( + block, + parents_hash.expect("parents_hash should not be None"), + ) } else { self.connect_inner(block) }; @@ -150,6 +156,7 @@ where net.time_service(), startup_info.main, storage.clone(), + config.net().id().clone(), vm_metrics.clone(), )?; let metrics = config @@ -188,6 +195,7 @@ where net.time_service(), block_id, self.storage.clone(), + net.id().clone(), self.vm_metrics.clone(), )?) } @@ -197,6 +205,7 @@ where net.time_service(), dag_block_next_parent.unwrap_or(header.parent_hash()), self.storage.clone(), + net.id().clone(), self.vm_metrics.clone(), )?) } else { @@ -227,9 +236,13 @@ where pub fn time_sleep(&self, sec: u64) { self.config.net().time_service().sleep(sec * 1000000); } - + #[cfg(test)] - pub fn apply_failed(&mut self, block: Block, parents_hash: Option>) -> Result<()> { + pub fn apply_failed( + &mut self, + block: Block, + parents_hash: Option>, + ) -> Result<()> { use anyhow::bail; use starcoin_chain::verifier::FullVerifier; @@ -256,6 +269,7 @@ where self.config.net().time_service(), new_head_block, self.storage.clone(), + self.config.net().id().clone(), self.vm_metrics.clone(), )?; @@ -284,9 +298,7 @@ where let executed_block = new_branch.head_block(); let main_total_difficulty = self.main.get_total_difficulty()?; let branch_total_difficulty = new_branch.get_total_difficulty()?; - let parent_is_main_head = self.is_main_head( - &executed_block.header().parent_hash(), - ); + let parent_is_main_head = self.is_main_head(&executed_block.header().parent_hash()); if branch_total_difficulty > main_total_difficulty { let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = @@ -384,6 +396,7 @@ where self.config.net().time_service(), block_id, self.storage.clone(), + self.config.net().id().clone(), self.vm_metrics.clone(), )?; @@ -432,16 +445,14 @@ where self.config.net().time_service(), block.header().parent_hash(), self.storage.clone(), + self.config.net().id().clone(), self.vm_metrics.clone(), )?; let verify_block = chain.verify(block)?; chain.execute(verify_block, dag_block_parent) } - fn is_main_head( - &self, - parent_id: &HashValue, - ) -> bool { + fn is_main_head(&self, parent_id: &HashValue) -> bool { if parent_id == &self.startup_info.main { return true; } @@ -699,13 +710,11 @@ where (Some(block_info), None) => { // both are identical let block_id: HashValue = block_info.block_id().clone(); - let executed_block = self.main.connect( - ExecutedBlock { - block: block.clone(), - block_info, - parents_hash: dag_block_parents, - }, - )?; + let executed_block = self.main.connect(ExecutedBlock { + block: block.clone(), + block_info, + parents_hash: dag_block_parents, + })?; info!( "Block {} main has been processed, trigger head selection", block_id, @@ -727,9 +736,6 @@ where fn connect_to_main( &mut self, block: Block, - dag_block_parents: Option>, - dag_block_next_parent: Option, - next_tips: &mut Option>, ) -> Result { let block_id = block.id(); if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH @@ -742,49 +748,14 @@ where debug!("Repeat connect, current header is {} already.", block_id); return Ok(ConnectOk::MainDuplicate); } - if let Some(parents) = dag_block_parents { - assert!(parents.len() > 0); - // let color = self - // .dag - // .lock() - // .as_mut() - // .expect("failed to get the mut dag object") - // .commit_header(&Header::new(block.header().clone(), parents.clone()))?; - let color = ColoringOutput::Blue( - 0, - BlockHashMap::with_capacity(3), // k - ); - match color { - ColoringOutput::Blue(_, _) => { - if self - .main - .current_tips_hash() - .expect("in dag block, the tips hash must exist") - == block.header().parent_hash() - && !self.block_exist(block_id)? - { - return self.apply_and_select_head( - block, - Some(parents), - dag_block_next_parent, - next_tips, - ); - } - self.switch_branch(block, Some(parents), dag_block_next_parent, next_tips) - } - ColoringOutput::Red => { - self.switch_branch(block, Some(parents), dag_block_next_parent, next_tips) - } - } - } else { - if self.main.current_header().id() == block.header().parent_hash() - && !self.block_exist(block_id)? - { - return self.apply_and_select_head(block, None, None, &mut None); - } - // todo: should switch dag together - self.switch_branch(block, None, None, &mut None) + + if self.main.current_header().id() == block.header().parent_hash() + && !self.block_exist(block_id)? + { + return self.apply_and_select_head(block, None, None, &mut None); } + // todo: should switch dag together + self.switch_branch(block, None, None, &mut None) } fn apply_and_select_head( @@ -827,10 +798,7 @@ where Ok(ConnectOk::DagConnected) } - fn connect_inner( - &mut self, - block: Block, - ) -> Result { + fn connect_inner(&mut self, block: Block) -> Result { let block_id = block.id(); if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT @@ -843,9 +811,9 @@ where return Ok(ConnectOk::MainDuplicate); } // normal block, just connect to main - let mut next_tips = Some(vec![]); + // let mut next_tips = Some(vec![]); let executed_block = self - .connect_to_main(block, None, None, &mut next_tips)? + .connect_to_main(block)? .clone(); if let Some(block) = executed_block.block() { self.broadcast_new_head(block.clone(), None, None); @@ -867,99 +835,99 @@ where ); } - pub fn execute_dag_block_in_pool( - &mut self, - mut dag_blocks: Vec<(Block, Vec)>, - current_tips: Vec, - ) -> Result { - // 3, process the blocks that are got from the pool - // sort by id - dag_blocks.sort_by_key(|(block, _)| block.header().id()); - - let mut dag_block_next_parent = current_tips - .iter() - .max() - .expect("tips must be larger than 0") - .clone(); - let mut next_tips = Some(vec![]); - let mut executed_blocks = vec![]; - // connect the block one by one - dag_blocks - .into_iter() - .try_fold((), |_, (block, dag_block_parents)| { - let next_transaction_parent = block.header().id(); - let result = self.connect_to_main( - block, - Some(dag_block_parents.clone()), - Some(dag_block_next_parent), - &mut next_tips, - ); - match result { - std::result::Result::Ok(connect_ok) => { - executed_blocks.push((connect_ok.block().clone(), dag_block_parents)); - dag_block_next_parent = next_transaction_parent; - Ok(()) - } - Err(error) => { - bail!("apply_and_select_head failed, error: {}", error.to_string()) - } - } - })?; - - match next_tips { - Some(new_tips) => { - if new_tips.is_empty() { - bail!("no new block has been executed successfully!"); - } - - let mut connected = self.main.is_head_of_dag_accumulator(new_tips.clone())?; - if self.main.dag_parents_in_tips(new_tips.clone())? { - // 1, write to disc - if !connected { - self.main.append_dag_accumulator_leaf(new_tips.clone())?; - connected = true; - } - } - - if connected { - // 2, broadcast the blocks sorted by their id - executed_blocks - .iter() - .for_each(|(exe_block, dag_block_parents)| { - if let Some(block) = exe_block { - self.broadcast_new_head( - block.clone(), - Some(dag_block_parents.clone()), - Some(new_tips.clone()), - ); - } - }); - } - - return executed_blocks - .last() - .map(|(exe_block, _)| { - if connected { - ConnectOk::ExeConnectMain( - exe_block - .as_ref() - .expect("exe block should not be None!") - .clone(), - ) - } else { - ConnectOk::ExeConnectBranch( - exe_block - .as_ref() - .expect("exe block should not be None!") - .clone(), - ) - } - }) - .ok_or_else(|| format_err!("no block has been executed successfully!")); - } - None => { - unreachable!("next tips should not be None"); - } - }; - } + // pub fn execute_dag_block_in_pool( + // &mut self, + // mut dag_blocks: Vec<(Block, Vec)>, + // current_tips: Vec, + // ) -> Result { + // // 3, process the blocks that are got from the pool + // // sort by id + // dag_blocks.sort_by_key(|(block, _)| block.header().id()); + + // let mut dag_block_next_parent = current_tips + // .iter() + // .max() + // .expect("tips must be larger than 0") + // .clone(); + // let mut next_tips = Some(vec![]); + // let mut executed_blocks = vec![]; + // // connect the block one by one + // dag_blocks + // .into_iter() + // .try_fold((), |_, (block, dag_block_parents)| { + // let next_transaction_parent = block.header().id(); + // let result = self.connect_to_main( + // block, + // Some(dag_block_parents.clone()), + // Some(dag_block_next_parent), + // &mut next_tips, + // ); + // match result { + // std::result::Result::Ok(connect_ok) => { + // executed_blocks.push((connect_ok.block().clone(), dag_block_parents)); + // dag_block_next_parent = next_transaction_parent; + // Ok(()) + // } + // Err(error) => { + // bail!("apply_and_select_head failed, error: {}", error.to_string()) + // } + // } + // })?; + + // match next_tips { + // Some(new_tips) => { + // if new_tips.is_empty() { + // bail!("no new block has been executed successfully!"); + // } + + // let mut connected = self.main.is_head_of_dag_accumulator(new_tips.clone())?; + // if self.main.dag_parents_in_tips(new_tips.clone())? { + // // 1, write to disc + // if !connected { + // self.main.append_dag_accumulator_leaf(new_tips.clone())?; + // connected = true; + // } + // } + + // if connected { + // // 2, broadcast the blocks sorted by their id + // executed_blocks + // .iter() + // .for_each(|(exe_block, dag_block_parents)| { + // if let Some(block) = exe_block { + // self.broadcast_new_head( + // block.clone(), + // Some(dag_block_parents.clone()), + // Some(new_tips.clone()), + // ); + // } + // }); + // } + + // return executed_blocks + // .last() + // .map(|(exe_block, _)| { + // if connected { + // ConnectOk::ExeConnectMain( + // exe_block + // .as_ref() + // .expect("exe block should not be None!") + // .clone(), + // ) + // } else { + // ConnectOk::ExeConnectBranch( + // exe_block + // .as_ref() + // .expect("exe block should not be None!") + // .clone(), + // ) + // } + // }) + // .ok_or_else(|| format_err!("no block has been executed successfully!")); + // } + // None => { + // unreachable!("next tips should not be None"); + // } + // }; + // } } diff --git a/sync/src/sync.rs b/sync/src/sync.rs index 274e8034ec..856ab5ffc5 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -168,7 +168,9 @@ impl SyncService { } let network = ctx.get_shared::()?; - let block_chain_service = ctx.service_ref::>()?.clone(); + let block_chain_service = ctx + .service_ref::>()? + .clone(); let storage = self.storage.clone(); let self_ref = ctx.self_ref(); let connector_service = ctx @@ -282,6 +284,7 @@ impl SyncService { skip_pow_verify, dag.clone(), block_chain_service.clone(), + config.net().id().clone(), )?; self_ref.notify(SyncBeginEvent { target, @@ -308,6 +311,7 @@ impl SyncService { network.clone(), config.sync.max_retry_times(), block_chain_service.clone(), + config.net().id().clone(), sync_metrics.clone(), vm_metrics.clone(), )?; diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 14bbddd69c..19d0a399b4 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -291,7 +291,7 @@ where block_info: BlockInfo, action: BlockConnectAction, state: CollectorState, - dag_parents: Option> + dag_parents: Option>, ) -> Result { let total_difficulty = block_info.get_total_difficulty(); @@ -422,7 +422,10 @@ where } } - fn broadcast_dag_chain_block(&mut self, broadcast_blocks: Vec<(Block, BlockInfo, Option>, BlockConnectAction)>) -> Result { + fn broadcast_dag_chain_block( + &mut self, + broadcast_blocks: Vec<(Block, BlockInfo, Option>, BlockConnectAction)>, + ) -> Result { let state = if self.last_accumulator_root == self.target_accumulator_root { CollectorState::Enough } else { @@ -430,18 +433,37 @@ where }; let last_index = broadcast_blocks.len() - 1; - broadcast_blocks.into_iter().enumerate().for_each(|(index, (block, block_info, dag_parents, action))| { - if last_index == index && state == CollectorState::Enough { - let _ = self.notify_connected_block(block, block_info, action, CollectorState::Enough, dag_parents); - } else { - let _ = self.notify_connected_block(block, block_info, action, CollectorState::Need, dag_parents); - } - }); + broadcast_blocks.into_iter().enumerate().for_each( + |(index, (block, block_info, dag_parents, action))| { + if last_index == index && state == CollectorState::Enough { + let _ = self.notify_connected_block( + block, + block_info, + action, + CollectorState::Enough, + dag_parents, + ); + } else { + let _ = self.notify_connected_block( + block, + block_info, + action, + CollectorState::Need, + dag_parents, + ); + } + }, + ); return Ok(state); } - fn broadcast_single_chain_block(&mut self, block: Block, block_info: BlockInfo, action: BlockConnectAction) -> Result { + fn broadcast_single_chain_block( + &mut self, + block: Block, + block_info: BlockInfo, + action: BlockConnectAction, + ) -> Result { let target = self .target .as_ref() @@ -471,7 +493,7 @@ where self.notify_connected_block(block, block_info, action, state?, None) } - + fn collect_item( &mut self, item: SyncBlockData, @@ -490,7 +512,10 @@ where // if let ColoringOutput::Red = color { // panic!("the red block should not be applied or connected!"); // } - let _ = dag.lock().unwrap().push_parent_children(block_id, Arc::new(parents)); + let _ = dag + .lock() + .unwrap() + .push_parent_children(block_id, Arc::new(parents)); } else { panic!("in dag sync, the dag should not be None") } @@ -500,21 +525,29 @@ where Some(block_info) => { //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. //So, we just need to update chain and continue - self.chain.connect( - ExecutedBlock { - block: block.clone(), - block_info: block_info.clone(), - parents_hash: parents_hash.clone(), - }, - )?; + self.chain.connect(ExecutedBlock { + block: block.clone(), + block_info: block_info.clone(), + parents_hash: parents_hash.clone(), + })?; let block_info = self.chain.status().info; - Ok((block, block_info, parents_hash, BlockConnectAction::ConnectExecutedBlock)) + Ok(( + block, + block_info, + parents_hash, + BlockConnectAction::ConnectExecutedBlock, + )) } None => { self.apply_block(block.clone(), peer_id, parents_hash.clone(), next_tips)?; self.chain.time_service().adjust(timestamp); let block_info = self.chain.status().info; - Ok((block, block_info, parents_hash, BlockConnectAction::ConnectNewBlock)) + Ok(( + block, + block_info, + parents_hash, + BlockConnectAction::ConnectNewBlock, + )) } }; } @@ -617,8 +650,12 @@ where //verify target match self.target { Some(_) => { - assert_eq!(block_to_broadcast.len(), 1, "in single chain , block_info should exist!"); - let (block, block_info, _, action) = block_to_broadcast.pop().unwrap(); + assert_eq!( + block_to_broadcast.len(), + 1, + "in single chain , block_info should exist!" + ); + let (block, block_info, _, action) = block_to_broadcast.pop().unwrap(); // self.check_if_sync_complete(block_info) self.broadcast_single_chain_block(block, block_info, action) } diff --git a/sync/src/tasks/inner_sync_task.rs b/sync/src/tasks/inner_sync_task.rs index e016ef1498..b893b3da89 100644 --- a/sync/src/tasks/inner_sync_task.rs +++ b/sync/src/tasks/inner_sync_task.rs @@ -9,6 +9,7 @@ use anyhow::format_err; use network_api::PeerProvider; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_chain::BlockChain; +use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_service_registry::ServiceRef; @@ -38,6 +39,7 @@ where time_service: Arc, peer_provider: N, custom_error_handle: Arc, + net_id: ChainNetworkID, } impl InnerSyncTask @@ -56,6 +58,7 @@ where time_service: Arc, peer_provider: N, custom_error_handle: Arc, + net_id: ChainNetworkID, ) -> Self { Self { ancestor, @@ -67,6 +70,7 @@ where time_service, peer_provider, custom_error_handle, + net_id, } } @@ -138,6 +142,7 @@ where self.time_service.clone(), ancestor.id, self.storage.clone(), + self.net_id.clone(), vm_metrics, )?; let block_collector = BlockCollector::new_with_handle( diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index f1f71f48c3..17c6c81421 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -15,6 +15,7 @@ use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_accumulator::MerkleAccumulator; use starcoin_chain::{BlockChain, ChainReader}; +use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; use starcoin_logger::prelude::*; use starcoin_service_registry::{ActorService, EventHandler, ServiceRef}; @@ -627,6 +628,7 @@ pub fn full_sync_task( peer_provider: N, max_retry_times: u64, block_chain_service: ServiceRef>, + net_id: ChainNetworkID, sync_metrics: Option, vm_metrics: Option, ) -> Result<( @@ -734,6 +736,7 @@ where time_service.clone(), peer_provider.clone(), ext_error_handle.clone(), + net_id.clone(), ); let start_now = Instant::now(); let (block_chain, _) = inner diff --git a/sync/src/tasks/sync_dag_full_task.rs b/sync/src/tasks/sync_dag_full_task.rs index d327d84c9a..9eee187011 100644 --- a/sync/src/tasks/sync_dag_full_task.rs +++ b/sync/src/tasks/sync_dag_full_task.rs @@ -8,6 +8,7 @@ use starcoin_accumulator::{ }; use starcoin_chain::BlockChain; use starcoin_chain_api::ChainReader; +use starcoin_config::ChainNetworkID; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; @@ -181,6 +182,7 @@ async fn sync_dag_block( skip_pow_verify_when_sync: bool, dag: Arc>, block_chain_service: ServiceRef>, + net_id: ChainNetworkID, vm_metrics: Option, ) -> anyhow::Result where @@ -198,6 +200,7 @@ where time_service.clone(), start_block_id?, local_store.clone(), + net_id, vm_metrics, ) .map_err(|err| TaskError::BreakError(anyhow!(err))); @@ -284,6 +287,7 @@ pub fn sync_dag_full_task( skip_pow_verify_when_sync: bool, dag: Arc>, block_chain_service: ServiceRef>, + net_id: ChainNetworkID, ) -> anyhow::Result<( BoxFuture<'static, anyhow::Result>, TaskHandle, @@ -325,6 +329,7 @@ pub fn sync_dag_full_task( skip_pow_verify_when_sync, dag.clone(), block_chain_service.clone(), + net_id, vm_metrics, ) .await diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index 64839b4924..e0382bad68 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -189,6 +189,7 @@ pub async fn test_failed_block() -> Result<()> { net.time_service(), chain_info.head().id(), storage.clone(), + net.id().clone(), None, )?; let (sender, _) = unbounded(); @@ -1081,6 +1082,7 @@ fn sync_block_in_async_connection( 15, None, None, + None, )?; let branch = async_std::task::block_on(sync_task)?; assert_eq!(branch.current_header().id(), target.target_id.id()); @@ -1155,6 +1157,7 @@ fn sync_block_in_block_connection_service_mock( 15, None, None, + None, )?; let branch = async_std::task::block_on(sync_task)?; info!("checking branch in sync service is the same as target's branch"); diff --git a/test-helper/src/chain.rs b/test-helper/src/chain.rs index c184d52519..d00457db33 100644 --- a/test-helper/src/chain.rs +++ b/test-helper/src/chain.rs @@ -13,7 +13,13 @@ pub fn gen_blockchain_for_test(net: &ChainNetwork) -> Result { let (storage, chain_info, _) = Genesis::init_storage_for_test(net).expect("init storage by genesis fail."); - let block_chain = BlockChain::new(net.time_service(), chain_info.head().id(), storage, None)?; + let block_chain = BlockChain::new( + net.time_service(), + chain_info.head().id(), + storage, + net.id().clone(), + None, + )?; Ok(block_chain) } @@ -27,7 +33,7 @@ pub fn gen_blockchain_with_blocks_for_test(count: u64, net: &ChainNetwork) -> Re let block = block_chain .consensus() .create_block(block_template, net.time_service().as_ref())?; - block_chain.apply(block, None,)?; + block_chain.apply(block, None)?; } Ok(block_chain) diff --git a/types/src/system_events.rs b/types/src/system_events.rs index 481ddd6abf..778bbc065e 100644 --- a/types/src/system_events.rs +++ b/types/src/system_events.rs @@ -10,14 +10,11 @@ use starcoin_crypto::HashValue; use starcoin_vm_types::genesis_config::ConsensusStrategy; use std::sync::Arc; #[derive(Clone, Debug)] -pub struct NewHeadBlock( - pub Arc, - pub Option>, -); +pub struct NewHeadBlock(pub Arc, pub Option>); /// may be uncle block #[derive(Clone, Debug)] -pub struct NewBranch(pub Arc,pub Option>); +pub struct NewBranch(pub Arc, pub Option>); #[derive(Clone, Debug)] pub struct MinedBlock(pub Arc, pub Option>); diff --git a/vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv b/vm/stdlib/compiled/12/11-12/stdlib/052_Epoch.mv deleted file mode 100644 index 2aa602ac288885c085b6f24d688ec4ea3d5c4b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u diff --git a/vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/12/11-12/stdlib/054_FixedPoint32.mv deleted file mode 100644 index 2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` diff --git a/vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/12/11-12/stdlib/055_GasSchedule.mv deleted file mode 100644 index 7d3359f06d3ca7172b76018eb71bb97aadf699d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8542 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 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/12/11-12/stdlib/058_PriceOracle.mv deleted file mode 100644 index b8584e7754d63d0f975320ee01be7b17fc125d7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ diff --git a/vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv b/vm/stdlib/compiled/12/11-12/stdlib/060_Offer.mv deleted file mode 100644 index 297fc8eb9bf27b993901390d58ac6b21e2ea943c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 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 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/12/11-12/stdlib/064_MerkleNFTDistributor.mv deleted file mode 100644 index 9b6c88aad4ff46335891d5a6b5f3c1745f20396d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ diff --git a/vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/12/11-12/stdlib/065_IdentifierNFT.mv deleted file mode 100644 index 44d5f27272aecd2f94a15d3e82260ffe92ab259a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 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 aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 diff --git a/vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/069_GenesisNFTScripts.mv deleted file mode 100644 index fe06059a19298b989fe5ef630ff59c63111237cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v diff --git a/vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/072_ModuleUpgradeScripts.mv deleted file mode 100644 index f2d215e295f47705bf56c11f22c759c52fc43203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG diff --git a/vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/12/11-12/stdlib/073_NFTGallery.mv deleted file mode 100644 index 7b0364a1abd25488c88f8e031c2c94f58b93d5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf diff --git a/vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/074_NFTGalleryScripts.mv deleted file mode 100644 index e9736e40d3098473a75e9cd4f6390736cb29f34c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} diff --git a/vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/077_PriceOracleScripts.mv deleted file mode 100644 index 9fc3054e32464ca19f39e147e1cd114bdb85369c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r diff --git a/vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/12/11-12/stdlib/078_Secp256k1.mv deleted file mode 100644 index a5eebc7a853d9d71538903b6e37234f5bf3caccd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz diff --git a/vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv b/vm/stdlib/compiled/12/11-12/stdlib/079_Signature.mv deleted file mode 100644 index e37f2baf06e884792c42fdb4bf03bae888c3fe3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- diff --git a/vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/12/11-12/stdlib/080_SharedEd25519PublicKey.mv deleted file mode 100644 index aa92ddbcab19b8289c18c5d75dd714715d8e0637..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw diff --git a/vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/12/11-12/stdlib/081_SimpleMap.mv deleted file mode 100644 index 5fda8be8e329a6340402ae254ff9ad5ca5502fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW diff --git a/vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/12/11-12/stdlib/083_StarcoinVerifier.mv deleted file mode 100644 index 1f79c3cc854a84bffd1177410c4789f649e19618..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp diff --git a/vm/stdlib/compiled/12/11-12/stdlib/084_String.mv b/vm/stdlib/compiled/12/11-12/stdlib/084_String.mv deleted file mode 100644 index a718cd408f0896e17de2b6da730b431dfe2a7a7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX diff --git a/vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv b/vm/stdlib/compiled/12/11-12/stdlib/085_Table.mv deleted file mode 100644 index 297bb011c407ad2b2ebe612110e13206bca96db8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N diff --git a/vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/12/11-12/stdlib/087_TransactionManager.mv deleted file mode 100644 index 6f1c313bfae799fc73233ce143be0b2bccc22e96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( diff --git a/vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/088_TransferScripts.mv deleted file mode 100644 index dca83ebaca3cf7db7eab5177d516a9c39ff491cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi diff --git a/vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/12/11-12/stdlib/089_TreasuryScripts.mv deleted file mode 100644 index 23b7501a57b223bcba74253c5b6edff659f4d604..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s diff --git a/vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv b/vm/stdlib/compiled/12/11-12/stdlib/091_U256.mv deleted file mode 100644 index e923809b5f9f07f184e1012756e5235bafd57552..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& diff --git a/vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/12/11-12/stdlib/093_YieldFarmingV2.mv deleted file mode 100644 index e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 diff --git a/vm/stdlib/compiled/12/stdlib/052_Epoch.mv b/vm/stdlib/compiled/12/stdlib/052_Epoch.mv deleted file mode 100644 index 2aa602ac288885c085b6f24d688ec4ea3d5c4b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u diff --git a/vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/12/stdlib/054_FixedPoint32.mv deleted file mode 100644 index 2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` diff --git a/vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/12/stdlib/055_GasSchedule.mv deleted file mode 100644 index 7d3359f06d3ca7172b76018eb71bb97aadf699d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8542 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 diff --git a/vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/12/stdlib/058_PriceOracle.mv deleted file mode 100644 index b8584e7754d63d0f975320ee01be7b17fc125d7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ diff --git a/vm/stdlib/compiled/12/stdlib/060_Offer.mv b/vm/stdlib/compiled/12/stdlib/060_Offer.mv deleted file mode 100644 index 297fc8eb9bf27b993901390d58ac6b21e2ea943c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 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 diff --git a/vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/12/stdlib/064_MerkleNFTDistributor.mv deleted file mode 100644 index 9b6c88aad4ff46335891d5a6b5f3c1745f20396d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ diff --git a/vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/12/stdlib/065_IdentifierNFT.mv deleted file mode 100644 index 44d5f27272aecd2f94a15d3e82260ffe92ab259a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 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 aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 diff --git a/vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/12/stdlib/069_GenesisNFTScripts.mv deleted file mode 100644 index fe06059a19298b989fe5ef630ff59c63111237cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v diff --git a/vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/12/stdlib/072_ModuleUpgradeScripts.mv deleted file mode 100644 index f2d215e295f47705bf56c11f22c759c52fc43203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG diff --git a/vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/12/stdlib/073_NFTGallery.mv deleted file mode 100644 index 7b0364a1abd25488c88f8e031c2c94f58b93d5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf diff --git a/vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/12/stdlib/074_NFTGalleryScripts.mv deleted file mode 100644 index e9736e40d3098473a75e9cd4f6390736cb29f34c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} diff --git a/vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/12/stdlib/077_PriceOracleScripts.mv deleted file mode 100644 index 9fc3054e32464ca19f39e147e1cd114bdb85369c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r diff --git a/vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/12/stdlib/078_Secp256k1.mv deleted file mode 100644 index a5eebc7a853d9d71538903b6e37234f5bf3caccd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz diff --git a/vm/stdlib/compiled/12/stdlib/079_Signature.mv b/vm/stdlib/compiled/12/stdlib/079_Signature.mv deleted file mode 100644 index e37f2baf06e884792c42fdb4bf03bae888c3fe3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- diff --git a/vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/12/stdlib/080_SharedEd25519PublicKey.mv deleted file mode 100644 index aa92ddbcab19b8289c18c5d75dd714715d8e0637..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw diff --git a/vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/12/stdlib/081_SimpleMap.mv deleted file mode 100644 index 5fda8be8e329a6340402ae254ff9ad5ca5502fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW diff --git a/vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/12/stdlib/083_StarcoinVerifier.mv deleted file mode 100644 index 1f79c3cc854a84bffd1177410c4789f649e19618..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp diff --git a/vm/stdlib/compiled/12/stdlib/084_String.mv b/vm/stdlib/compiled/12/stdlib/084_String.mv deleted file mode 100644 index a718cd408f0896e17de2b6da730b431dfe2a7a7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX diff --git a/vm/stdlib/compiled/12/stdlib/085_Table.mv b/vm/stdlib/compiled/12/stdlib/085_Table.mv deleted file mode 100644 index 297bb011c407ad2b2ebe612110e13206bca96db8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N diff --git a/vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/12/stdlib/087_TransactionManager.mv deleted file mode 100644 index 6f1c313bfae799fc73233ce143be0b2bccc22e96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( diff --git a/vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/12/stdlib/088_TransferScripts.mv deleted file mode 100644 index dca83ebaca3cf7db7eab5177d516a9c39ff491cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi diff --git a/vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/12/stdlib/089_TreasuryScripts.mv deleted file mode 100644 index 23b7501a57b223bcba74253c5b6edff659f4d604..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s diff --git a/vm/stdlib/compiled/12/stdlib/091_U256.mv b/vm/stdlib/compiled/12/stdlib/091_U256.mv deleted file mode 100644 index e923809b5f9f07f184e1012756e5235bafd57552..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& diff --git a/vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/12/stdlib/093_YieldFarmingV2.mv deleted file mode 100644 index e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 diff --git a/vm/stdlib/compiled/latest/stdlib/052_Epoch.mv b/vm/stdlib/compiled/latest/stdlib/052_Epoch.mv deleted file mode 100644 index 2aa602ac288885c085b6f24d688ec4ea3d5c4b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2724 zcmZ`*-EQ2*6`o)I%*-yy6}ei;vTQ|)<-~T{IN4SXU=)EZw7qCr#JTDXLGRKsn_W^^ za;-S8kf$h$T(-}UH^@aV3-r1`uKEP+H{@!~#O;7HJm<{$KZnEl^T7|=A%tj3LKYtI z`+uk6Kh(%?*k7poH~v@G`-cjhAJix6Tm8uVQ~%o=d>JrCsDIwyvpbjMpyhJljh~a{efnA0YMgtd6=wTzWw2_ZxM*)p3n%HG*mt7786ASHW zPS`P6*a;sH{;m*&-r+++PXkKm-4N(|c6r|}_v~^QM#Q;4+M&dG5GRz#4|W0mjK|>r zxn=m!E)TIdA0?LfF+efh=hSh3!8vh%3A^;nGCc|@5ud07>gV+ z71Hsz>k)4GxkWGp)q--jLx3|d2)jWYBVCVDAVWcgm84b}+=2^1CG|M7ViXHy)PWu! zskQp>_K6Ed%DGh!e&JfB7}R6T)e;OoVb;6_<@sebdl@{b$~rIWbq(M}aWQyOt@2Oj z^HpBgdF`C9R@JJO=dbdz5x>itm(K4mo1!ZHFN$SeH`(&iZ`C}_nr!e{e$^r^x5?&z zTGvgQTPxuPxyb5tQ7nrlc)qA+U#43;>hK0y(d!@Qht*vj?tdA^zyWm>J$c|Kd^S>4mDin3^mY*BojJ00r?v8}F+bR^Z0 ztc!kAHQB;?=B&%vBCn(M9hvpFVy1`T4VFVNt$9cIGK6vtGhyg#x9s zwqb6}D?nYTdLgz!MO{9+0B6;@Y;HcciSP0KC>00o?*GZhUc33flH$Vix?1LDj+R_i zuUo`jt{3UNcqPiJ%#$Tb-|6kOSYnmEMU8t-Z2MDK{ZZ$vEx@KpRo8R2+lD_*Wmr~M z>9SZXiaJN4=JmLzw(mnr?j@x4r>m}2o4#x-ilenZDEIOW&b^&2G~Yu1!g*XJn`j6GBC*y3 znP@unqQvgdL=ExciGoN^3pT zCW0Mom^gBDV5Fy|4PQIjH6t@nBR$e8^y2A6@#(~6)BUlDw66y$(m1-KKu65_?<5*O zNX-=u2Qlc#@=%@;G1BskY8ng@8EZ-zm?_~hitZ6GGW-^|D36(ds`0NBiIS;twSYr}BL@mMjfj+A#x+|%4CH=^2!yfbdJ zd<Lw@Rw2%H|d;eH`sNZl~={7Dzgt%-c9m6}-O8F<^iS`Yu$832y zOa?ox5s?-j z8!mUy(_C}rBLTyc=mb957N-jLrOk-m-GW0JC75?GPcg@syOO`uL*7h+ zjwSds-ZHF7pc8o)IW)0mxe;+$1}4>kIxU&8>aVzSD~f;MR{R!! z!erWF6F52dxd*|KxR1srb+>2(L@~`R4$azf?gc!157Dhl6Y0p4Uj9|c}uoh53PaYbuFvJtMfDm^4 ai9H*dQZzUMu7pt-nk=5fDGVuomViI+@={0u diff --git a/vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv b/vm/stdlib/compiled/latest/stdlib/054_FixedPoint32.mv deleted file mode 100644 index 2ecc1abb826e4f91bc83f633ac7e73b8f52ffd05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595 zcmZuuU2YRG5cd3dy?EU;RH+125ot@62P%cM39s}4c;*JHRo6`|ZIWu)lyU~{PzksV zN8kz^qKw0`0;%&b{yg)2pU3vs{a;z4l(L{0nJGQeF9+t$7f63VKlur7?Hy|O9q(c5 z8>0|GDWep|l~59sR5eIS1VM_D9!WwXNEHxLsu-AA0lW9%)|VevVcDFWChzKcReu~@ z)V^qZep#;;d0jMNwJfUL|BIjUo1$8KTZWrZ`h5L*x;6KWu-k@Ptg9wmSD#}`Z@B%i zlr@xjQP0;4zifI{ScE3`w-?@*zLcB%8$hXV_mp0C=&D2W4uuwR|Gtg3Ag=<3;=}Nn zRkO+Y*y#*rli}F0<Qya%-snejIn3R9f` diff --git a/vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv b/vm/stdlib/compiled/latest/stdlib/055_GasSchedule.mv deleted file mode 100644 index 7d3359f06d3ca7172b76018eb71bb97aadf699d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8542 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 diff --git a/vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv b/vm/stdlib/compiled/latest/stdlib/058_PriceOracle.mv deleted file mode 100644 index b8584e7754d63d0f975320ee01be7b17fc125d7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmZ8gOODe(5Unb=+wM>N%Ljxw28--r!vcYjSX;*FWGqE-WP3Ck_AEIBpjmJR4!{xE za04zuwVfzoEUT*Cd-ds-%U_@WH4*?cf+U#*t1l?NK=Ch;>E9x>f5Z=* zeU`+z2n0a{q#|USS&R?_AcK&Atqi=4M94N4p4URMHBrpgDzDD8x5=2Fj`_J^hUUyg z5rr8~V9!lFjK#=mG55$s3wF3z@zdlV z?W%h3#G7ikkyD&r@vjftw%EC$?CWaZ9Cqcls)x4AnnN>`n`)@azP;^gw|Y#J?l5%s z&zk-^tMpk7H~Qo@qI1v~#eKXvrC-trwZpykkCjZ|m^%tFn@JY9AaBS~*jjkX5l34H#0a&>P z3khVfAY=@}fWb8qpVY)^F&4l( zWzA4XK~+=L;2Fh=6BZG4XfdJ+2Zv##1n7Y{QChz~zOAdL(zM&m;}xGv-{${loZFI{ zE>z92dCBd*baC)?xp2DTgD?4`KOA$3-D$Tw*YADacDGYl?=Syot?Q!|Q5P{ZGZB{R RCPbnMiOhgD!UHc2!5^eoGq(T$ diff --git a/vm/stdlib/compiled/latest/stdlib/060_Offer.mv b/vm/stdlib/compiled/latest/stdlib/060_Offer.mv deleted file mode 100644 index 297fc8eb9bf27b993901390d58ac6b21e2ea943c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 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 diff --git a/vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv b/vm/stdlib/compiled/latest/stdlib/064_MerkleNFTDistributor.mv deleted file mode 100644 index 9b6c88aad4ff46335891d5a6b5f3c1745f20396d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1338 zcmah}%WfP+6uoup*;O??50A(8*l}Kqz#@rci=>evArU}qjF5uZwt8mDaVwtbQFl9$ zcliLez&hW-5AY8N);uIuELd`DW^95G;x4*wRh@gzsjI4fbMTu}0MKF3!`?1`=Tf|3 zWBDEXf$4Af_h^2SJ@vahv|p*jeXY=ctA6C2KN6AtnFy+5fWSe31O*z5VVX%PP}7$5 zo(U#9lqZA(i=1h2B7Re;xE30^+Mw3ZODrw+9c}5MB`<8d%NXcU&(e2?CZKkA`ha_B z3VcjkVtElyn6=0p!Q2coUI_J*EJxd-Sc-DqGfFSr$Jl@%iqM-*2++K9ZGIvrV= zj-o55#oJNSbt674nlrZseO_19DJQ6URz6>Z zT74O&gx>x)MOiIeSWK%~$j?J@nv_jGFBff?+3PG%PiW<7u`DL#dD&i?lh78kqAfZR znZGLAvwT`CO&y+-u~54vv>TG#zYKMGdYLbyAva&0Ag8=dr{_gE53_tyw)0{s!+c51 zZIw?h+t8?DxeSY$npAYOSGJhV>d-WKb*iQpO zth_63%AJlnx>~&&v&5OVN{t+lw0a-Bw$uYBGE1ZW0I0bRB~{)0zurBHz4QOoJ1d7y j>^r%Qq{`q<>KJuRq?iJ=My4c5t&y=c82@B2U<3FIr$nj+ diff --git a/vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv b/vm/stdlib/compiled/latest/stdlib/065_IdentifierNFT.mv deleted file mode 100644 index 44d5f27272aecd2f94a15d3e82260ffe92ab259a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 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 aM^RFQqwUy{ICcLQM`*XeuHk7eO7L$*Y=jd4 diff --git a/vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv b/vm/stdlib/compiled/latest/stdlib/069_GenesisNFTScripts.mv deleted file mode 100644 index fe06059a19298b989fe5ef630ff59c63111237cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmZ1|^O~EDfq{XIk%5Jog^QJson2Iy!%2WAh#x4*$iM`|jLblSnTMH+i-|$dJvA@2 zIJ4N#EhIR(D6^oXm^J~UCWs*m>(#4zugWQZyLo980Hg?(>_*&v$hDRIQ~e#kQuIS)>ZeGO-y$=g znCxeE=_S+R7gNw65C~w9pfrGv5Ku1ccuEc@M1ZIvCPaor1WZd%q_xa2LYAvILB=B! z(tJ!2h?6u0xuyj&ZI&1cg=8(Y(y>k?Ndc%VD=S$DmPTL<6pu|}3=kzLLjf>erRIu> zlPGWkLfII_@>$akkIU}4@A__EZuNtz>JIH70;isM-}$|I(VVr;%a`3d*T!T0gx0To z+4rs+u?uw3&#tQ99?qI}c@@vep^Ln$TyyTcDtA-Us_Ym4 z``vICgD_4&;-R_ z;l{gNcRmkvO*#)6HhcX;5q-g#ATrW25~kDN4BHKRM$QZv%;9)^LL$dgxC@4IvTTIm wT!aSzVAnZXN?@f8-Qq~527-JQ-AN#4i=)FmxDQ037WEh!IszkdIWijl0EgXzIRF3v diff --git a/vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv b/vm/stdlib/compiled/latest/stdlib/072_ModuleUpgradeScripts.mv deleted file mode 100644 index f2d215e295f47705bf56c11f22c759c52fc43203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 901 zcmaJ<&2G~`5Z>88J6^}JlhQ(k5Qqa8PCfY!94jgXxURPG*0qS&mVeUr33vi7+>m$# zj)><$;w2cz2`UA#vSw$#@0)L|)qH;VQ%OR|1V~KdfE}0o14QB@EWID_jrm_hEWe5% z{4PEL%79S92q%I_;^85Tc?0evLnai!BLN$t+U6lgcq%YN5(xKEE7b&f83Y*lOd%RW zNJQdI3CyQ6@VrGF5jaSYXZK)^g6RUs9>y_YM?TiRKk>nzMzbi67eg}(1(Tr&C}Y4w zynu$3;RiB=38BdTrsb9g3Igz-0T`9xgn$ARq6x)>9h_EKzqW7dyfK+wu9~9mTKS@K z7ey{#HBHsD@^#%6l}pad>b=SBn`?J!943ui7P+&{{ay3u^wLykO;uN|S$pqn(_-gh zW!%cHbw3W#_1d_n_S&xc&gybI{|`>rTLb%Gci+`)PLA9DyezuA#9+MGQMZRZ>g&w> zJ*({+MDv^H|EKj;(Ov4YaJnl>d-xB3y*W9|ZKwB!y1S}$eQ8=7m@I2-+v@7#XtjM* zZLf>A)8{vOM=58o^k#gidEK9{i}q3v*r?~OayBm9#;l7>npI?6rNv#meSrDd zz6MT!MI`ZXH8JH16io@GL*YkcC>2v&i4<^k5EN<)rj+ublzUAQP%!}W021|tPth|> j+2bL>Egw%3rF_t*B1xzm)X}IG!uLore>R$>5^3@aR2#wG diff --git a/vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv b/vm/stdlib/compiled/latest/stdlib/073_NFTGallery.mv deleted file mode 100644 index 7b0364a1abd25488c88f8e031c2c94f58b93d5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2260 zcmZuzTW=dh6rMA;UC+#}FYC4A#7^uqy_a%%C{w85r9hETK?1?!%856LMdFR@ZPWY& zp8692@fY|XJn_s!ANdPNaAww<1c9ZUGw1f5%bcC{&%-~pB!omjVj4eWPu>9kkvVf`EN%Onk@O@ELMLoClp}48$eeU^ny|V*A z_+6ObdT+Oh>-`%eTpx^3BH!A>y?l72)@egrwbs zr@gelv5^dtO}@otI^?6I-y0?azMTxBopcx_(MB}xjW;HVPMhgwvYSMyNI@C`1_UrF zjb^k#8bC$8K@kTAxLiuqdgrB#ib^X0+pYboDgsOzCKx^lj7AkSXsANCIH0L_lYS*` zRrp9;I1_=Epq(&S`+%%L8iYD6V&Dd?8zgdhu6-QnRmNAl8iz4&yap4lIv2GffhQi7 zQHD)Hcm@(PLWQsF5(Wh>VpR~U%4T(rtj@mfm!J-y>cX0+=bwJ_+4STjUtY*hmdj$f z5}&-x7bQcv{PL`v7Yq6I{AiIc<+u4kSuD-d{H$2bOK%l@H!qK8%jvo20))5FXMW%me(nodXp84 zTomWn6FQ$YFQvKb+e-UZJew|NCwW}gTB*dm%uiQpI-4!?)e3vkTIA=M*Ja1vLHK0l zNxnELkE2CVW@j(@Nmtu@KQm2?wD+HFh5yrqZ*e5hrQiNP06!^cE)@WrD5oB)f7<&@$L zhOaByp^J3)vj0&{o{;y66_j~{01dcCWDQnQckRK}P>~PudKN)I+UaeD4n`>P2n>(G z1_mJvzK@z|`WS4e#UsqXHmJa>AI4ufFcYiT#cfzcRgC_j3SCfRl(|f0!UsA8t4tuC zBe3Q-Bd}=UOtrF+q2iu4_*tlaeH&wFYILm8_F1ISwxL1{-mpy*>F^OXB91UYrenjk za49~fwxuK6Hd1_11(RBuKnK*Naq})#FAP)cT2dMtX**Q2tH_2b{3zMP1_~y8DEerk zsg0@fFh0Kt-&;G<>yDy{Upas~yX%9nU4H^DCRPbprHBRAGmHe*gBhcgW2Dsfu>P@T zV7snt8v5gk|8+FgSt1JB(}pv+d#8@mui}91V=VB^=a*GtL0r*2z9)K4=V~(|Vp?F_ z4SiLB%K<0DU4jbF0hq?gpxQ;cm->UoI&fA3q5Q09_7V92zxD>~0MpvUCT$_ZaT48B z4k`Yr0Jx8{irSH3F|*q|6gmJLG|lV|b^tG3oW$5Wi5LwLWK8j6iy*=~#&$wmjt^r^ YY{yOv;QU+z0=t7>eY7Veg_Ne`A9jWzegFUf diff --git a/vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv b/vm/stdlib/compiled/latest/stdlib/074_NFTGalleryScripts.mv deleted file mode 100644 index e9736e40d3098473a75e9cd4f6390736cb29f34c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmZurI|{-u82%qg+L|B=PU7m~;N;-wq??QGrIaW_TT2W=j^i;rf@kp(K7#1rzkF}_ zFQdCw0FWRE785a5a%Pf6NUzv4Hyl4B1OlW)07nq?QYhaFRfObiG7FZB2H3Eq3^MGm zm)liQRn~<~=?<+wb-s=&%F?!e;B0+7+uYWz5BWZ}Q}2rAw6o5{Qf?dX!th@;M%~XK b%ID+1hkyc*HOztVLy_o3r3Q_f=0xxSXZzOCbA>>@`L(}iT4%1p#Cm?DD%rt z>>vLpvIaqb1O*=Wpus@kc^VKk2%$kK4O%m7Gz5VL2v-yd6pUKQ29e^e=eF46EpeyR zNAUaFfZSOrF|!16x97UVUJ`@aPx_z-yBU~+WFLaV!vppYLW(G*9onM-JrY)e@ML7A zRI*z_N>^D#8QV~VT;DD9Eqa0jfCwN!G)VV4nJ_4Q8=yoZP`X;6Sv?t10?|PVjq@-b z8b8{4gTwvoq&ogoOsnI%x|mK*N6mCLZ)2|7vT7G?2gh%UYO*LM<$2k()4EDFnqC*R zeP3P|&A3y`)3am$bULZZW^mRNRa=ba-1Kho@p9UJI+?lRK5;g^D(l6(Yc}U^yTjA+ zrW`HiWnNeL$W54!RwL%Krk>Sparyjzf_(YvNhdDz#cW)#7h80>)@CoZ_^WzZ=9lZR z`LauUv?Xd*S=SEFw#fNSmCw4QdA*AIkYe6NUV99-JeF5$A}q$^rfl21zR>HmSyf&; zb|Xe_(?GH&b-Im7A3J1z_dT}8{D>9vgYiAPzYtIXdOb)D;Sl)W<5CfDfkJRW zeL@l>Ns&iBX)+X0NR);)>H*&pI}}2)FlK|)TFIa!F_c0v*moe`n>DC}$6yvX%d}?| k!;0k46F$THP8;89eW4jV$R2(@S&Guc#_SMNZ>RC6T>_9S;|NoasV17LKlW71*5eiXgy7NMQER=t5e&KISE@-1K$-1A^GlL+2 zK!O5Ii9rKk0MY1FDg%|AyZ1iREECCF5bNzxd{Bem;zxuJLp{pLVOB zbW^=v&AZ{~`)*tFr{+NZ*zzQ922v9Koe@Ahw@`tT8C2+WC7fBtyN07uW_aHTOIALT vkJas5S5!%tIE7ha#S?j&n@m+IBOcvV+;gOg!c_WHo~dUB%JgYrN-N+mj9+0} diff --git a/vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv b/vm/stdlib/compiled/latest/stdlib/077_PriceOracleScripts.mv deleted file mode 100644 index 9fc3054e32464ca19f39e147e1cd114bdb85369c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmZ8bOA5j;5S^K%shWTy=*p#_E7!e+f(HmC1_?-8(sbd{qj(ID;90zcX$x99i^sel zyyxa41puT7obfH+i{vm%vj zC-00s`rO>?*?3olS`MUzD_qc*Jmk7A8*l8~+pDXC^}6gjqN$>5*W;YFMez64DBY{7 Z@AM=NA07h6WIQ#$r diff --git a/vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv b/vm/stdlib/compiled/latest/stdlib/078_Secp256k1.mv deleted file mode 100644 index a5eebc7a853d9d71538903b6e37234f5bf3caccd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 633 zcmZ`%O>fgc5S^L*@Y=g^Y=|m^-~boSptj1fi1yS2ACb6PuDb-Qi5;z-R*^XIA1cnA z_z@gA@MHKBOdKKsg3)R;^WNLH((HVG@WW~Vun2}sDh`h1xmK@Fqg(t)=99?u7coh` z3g^Cy52J5VvKE0rfQ$fC1Vj-srWu8ZxH5t@v0~E%SYi!;7+HsasdGT%bj441Yc~kc5;2-@h-H> z;`p$**z~@8_)kgS?%2^U)>rMd4+npAMcwp1H09#{4(|4^?j9K2@BK~fjnzQwg>UBl z`>bxR%SC-zlwrPJ`lgp%yY#YY8_$FPJy7t#b|K8ko*{HRwIOf6D|RVNA;L#et2lx@ zdSc-Y@K~9W7BZL+aXq?gc|9y;ILU=mPFf|$R^~88CsaNqC#)dBOCkSfilp|YIN>GX F<|joYadrRz diff --git a/vm/stdlib/compiled/latest/stdlib/079_Signature.mv b/vm/stdlib/compiled/latest/stdlib/079_Signature.mv deleted file mode 100644 index e37f2baf06e884792c42fdb4bf03bae888c3fe3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430 zcmYjNJ5Iwu5S^J_+xx>xXlQ6SLLwy!1qB5X4FZIe)(ZB3tPnf0ogg2A3(!$<1P;X| zn7D}8)sEi0H*chUU+2F*1^|O#$T)R6*S98mxJo|o6U{e`!Vf))jzFM@5)_CDfEETK zQUI}*l86jOkg`U}tcx82da^*l3#1e05uzlv##&2GE_y(NC;-)=cSHl@2#_##c94Y< zU_luf9(4OuzE-Pl*RZ=;-dz_(!)+ViZ@aRt;w9%@-B`{W&g(rlXIxBYv&m()U%i#Z zs^e_CdtP&&9(iucmwqG*%ROgD->Bt$JDttflT5bAgelvMH`}h)Rb8>F_+7X4hC{(^ z*VKJ>Xe6fpM*@(DMjkwlRZyu~fOZQE6s2B`lLZCxB)=yhc}f!0VhE|19L5kLu_ZzK LXfc>meL?UCh08)- diff --git a/vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv b/vm/stdlib/compiled/latest/stdlib/080_SharedEd25519PublicKey.mv deleted file mode 100644 index aa92ddbcab19b8289c18c5d75dd714715d8e0637..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 615 zcmZWn!EV$r5S!rq_iL*BrZs(APz;U5XTDj)C-~?5V?uzMl8wVIM9Af4}1XJ zIC14~_zA|#qE^BOYv#?Hc{BEWee~NZ05Aw8m0Ik-%FdqT<9E_GqEg?)j`=By@|XCu z_#q|p5C{ZF$N)58$&g|Ua)v<*c8heGOad-)4cSf(Aa@BtR7C+)69MiSj%J8xh*W6^ zh?$0v3#t+|q9CO?c>jFqg6~edm&eCPuiwrWtG+$+*ZE1?j`I)~Cv#l-A@;3{V^F6d zjA1Iy`^&+_Ie3*U9}0ifPjPBo6dyQuovKVr6UOMGZy2(-W>aqLejJ+Ctq)l1U81r7 z#;x5&zv|=lV}BKcYhy}n?$Wn$RX==vz5B%TGo6m05cS(-bIj!o44br%9Ie+8{ z9{wY`+lf*W%st$8_kX1M(#K`$b1TfBbN8Uoe(Uqo0ubeftj6?PQdl?y!sN)z!gHu# zpAt)zIU1<5J>G@|S*)c6OD}-LhN~>p>KP=Q(l*F@2J2kPtgwJN)cUD`3K=VEW6{Dc J9PnPS${*!1fMfsw diff --git a/vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv b/vm/stdlib/compiled/latest/stdlib/081_SimpleMap.mv deleted file mode 100644 index 5fda8be8e329a6340402ae254ff9ad5ca5502fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1281 zcmY*YOOM<{5bmme+1-B3?7Vh2OTsDw4oE9bNoP6dh&Ui|9XT^58D+<|Jf4L8H7HVk z0^$V9HGd$ta6pI?e}XD|hJ}{8s=Df{M^{(Bx%k@~Aw&uy!e}n<{{-qc_+0-9AHlwp ze?jzCkIc_{>fY;_|6Qx(5B))=zr-f{HD*T;D5OvpNv1V$L<@A#0#GSoP%^VhGmFqL zi@`E;wug9(_>{zsS*kpK! zOzt_toK7x?o&iyLzF_>l2W-9glw{+bOY+oZF3(5#I6uiJ`P9waJYVFe`E)Y9oK0qz z(t5y{%H=K498M+{U}C_dmCrT?S_c4l1PMnt$UX*=G8Rk#$>RWF9e6PytfNE%WpA@R zp3G=SP2q^}W+O6EQc?jVpd>4CPCI}YBek^d5LwC?=tM?xVkBMZyUn(_DZeQi^R#X2 zc4xkAx=me~@5@zJxAy5xxh<e8t<6nYJ?~zaw%pdQO0#cvW!p(|(XWb|eW}((SJ=&NS=CiJ+Ek?6 ztd~Xme9xKVX1{x}d{(SpGGte8OI!ZfwZ*E_Lk3nUcJ;g0MY9}|D3+t9ZhFstEbj=& zh)0u`j1EI91Y#bG5xpP6_}DPv=}`!qE}Y09kyl;@k*G`sP+pJ7X2sZl;l(3H;u8@N zX}H9A?npsUnaLnkq*I0hK~ou2f?hnPmk;_e5MGG+8N(C2BHa*P9>e!OihN|BLY(z?_yg)_B2xeW diff --git a/vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv b/vm/stdlib/compiled/latest/stdlib/083_StarcoinVerifier.mv deleted file mode 100644 index 1f79c3cc854a84bffd1177410c4789f649e19618..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1988 zcmY*a+in|07@qTvXU6ufvq_vZ&B5+LT!2Vhkhx4tq##mS)m0^~R%>UIm^xl-y-8`W zxq@3R5fbl!cmu@4@B~~S@y$9;%qBaY|MY$TKl9J*Z(Dy`MF_E!IOf)P=Y#l#w&m~i zPa6EgeiHhJAXGmGKT`9n{>c5M|4{XR8zBNHVT2PwBvAn&0bY%_77)$@3cG;^v5XVq zge0U6XVUPlOtT)1T^$j*w<0N#op_ZJ(Ou(I z$a|X#n)~VwvUtEb;hh~A>BCh{Nt1^>3YzU!d!-$=Bi`H&w^#YvE>(t6O}H|KVd^Er z7Bfz$re5HbGJ`(`FHNatTtm``L4)e=NiRdy5`p;i~lb>W!f0`Dv?0Hta9cM3# zJU=y+y_((~3}?wnHUmvM9(~A?bdHeBJj8=xelfi)vt)KLO%PcogZIfaEzgs5a*|8{ zo}446m5MZh^F=;MrbTu-`hI0tWI#y9qcSVfapl*@E~e#sfR$D3%2@`$QJJJ=a$4jU z$;E7-JznxQdvB`ARXV=R+$4K<6SQ6lRYl1~RT2nMQ?t_hr024oj?-awo{vwm!d&@P z^u*KFVtVUYpJCIrK$LCq-vpLrQ2Cv$h_Jo zELfS%lB;Ta)Hs`*mFH@Z7e)R~c&6I)67>$!;agcPj-H*TkCR8AeXh%V9*Z-!{7|&q zwTzY&PsjNH-9xy}uC-jB-pu^j^p9a1?d2}`YJyFfq+{P6hONfSwquk!*Y9liUmm>b zA12QaUwwUin7lkbcy@U7;`qzMSIN;q|A;W!o*xFv*oI-7Xm!T!pryBPEEvD5*|uRj zn)x)8vcz9N)e|*n3(-)wo>&$;abt1PkREY?ktXPhPh{N?BYQM<+E4@SIxd9yvmQyY zW3?46>bUG$VYQ!G?o^-}s>htL^2U43NvSQNmg1x_28i|hx1{WL$_+;bINjGY?XKub zhb(v`kpjj&u_`w`4$f)UvLy;;`I5w?unZRsAsd|va`zhYzSvfaxi_r4C4Ruhj)RGH z#g>fge4q4$rAxR}HZ5mh55z;^S61|7Pqrw-^-I>*486&H({XUPD)^9U41!XT9BXaB1l eYH_N;dRPiHb6~-4J)y&)rqp*2N4qkL&*gtF>KfPp diff --git a/vm/stdlib/compiled/latest/stdlib/084_String.mv b/vm/stdlib/compiled/latest/stdlib/084_String.mv deleted file mode 100644 index a718cd408f0896e17de2b6da730b431dfe2a7a7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 936 zcmYjQO>fjN5S{T?9NWppyIVvN7laB2q)Obv58%uJiR)_HP1}ezyGl~TiW3KZ2)_!6 zKfr+#ToDq_DbQNVc>Lyh{Op;0e)!!RBJxaDSSRPL`rz~@exLp3uWY}_%6yYc_e+}a zQT-6bPb0u)5+sQ-5+Y-)6%f(_#z+NRBc)A|Tv;NQW9c2d-~&-1;gu5DtSE`9Fe8eg zB&i}Z$#P+scy0{IVqtO8;@}?al3~kgR>f*wE=rMELYg!=!Nw9BCj{o?N(mUJFbkkl zzzHegNXanZz(L$|vbcoHp^_|iCRR!`2p+ijZntL>R-yP@gFMca){->olKrx(rX+ld+f zWBV zzHB$xw;mfcY_A&CZMz0#ygOLhROvJ%s_=KgYJS;2b#) z>Ns`lG`xyFlC1QDJSeZ!BLD(-(E4#6wAbp1e&)^11-yYq o5$&8rK>it;J!)k=&8e1wwX0P?FNz{5aZ^y~EMwd~aZ(-r07|QG%K!iX diff --git a/vm/stdlib/compiled/latest/stdlib/085_Table.mv b/vm/stdlib/compiled/latest/stdlib/085_Table.mv deleted file mode 100644 index 297bb011c407ad2b2ebe612110e13206bca96db8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1107 zcmY*YOK#La5Ur~I_|yK+C#$?!Y3-iXB4iI0i@H z3~Ufv4nTE#GLu+pSJkUmRbAcH-=6<98USJhNiq?|7nDEnQ2oR^^nTGj`M+hR|H&8O zy^76OC8O`^55=E-ncVr30}&`emK-jSEdfZvS}0h65{v_Ap`7Vy&q-hV77l;~K^Qoa z5upVm5jsw6T&5%%Go@yfS>#xG6kBqEhJ=I(CdAdIIa@Snr#<7>;V3MU?9}RFJv?|= zJ=l0yt*+<8tZLfxdc7DX8~icmpY`2P&%0IhpxE4T)2@bPe_OQ|m&0xT#IpXPS@xIJ zx;vY-XP;WPLFK4=(RLq)ndsW9CRP~`$rEO;aVlMoCK$_bPZ!ZDADn^+Fs zH*f@d#6t}H15}2IB_7+!Bapn*4yGd>2sS5K2q8w|jS;1IDI;qcDy556sbQ{|qY`1DU R_8UnzaJ52%$|3Bs9b|kl+gYkFqvQ(bfbdDV&2Na0-sVVYmbd(gYIz zu>I`kXZwBqC29bu5gKaFt93iOd&A}f7xEMDa|3|^0WYzl4D0M@Rx(25NJMb94cbNB zC+pqTj>b=^dN}(2FfS-3Jw7l0Qy#s0vioZr)r&sbL8V_2l}>ZQ`BxQ9@7)lWdy`Z@ zg$0vUV*don;N diff --git a/vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv b/vm/stdlib/compiled/latest/stdlib/087_TransactionManager.mv deleted file mode 100644 index 6f1c313bfae799fc73233ce143be0b2bccc22e96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1307 zcmZ8h&5j#I5U&28=^l^WUavQhC5e|Ldx;bY3CR)?lCv==2Ox@9Tt~B>j(1pl#>`I= zcmp0G+_-S&4Y+gWEqDp4?H$F?(zL3ozxwK{9##K#`j3$S5E3|}XPQ0vng33-_=EgP z)L-;Z9{i#P^1b>Z`dhuHh$BFO0S5sR6a)~01_KfFAcj5+qKFe9VWU7!UW${f~q$^jc*>YL7MI)ZA%jJ#P?XTS1w93@Qb($A1vff2mxmlJ~uC84z=G$_4 zE$3BLR<(VZE^m%D@tBFR}b55mSTLhF0ayc(x9R-)>z_g zBzrv@*Q5wZqcr=Wt((O88yfu=Q|+!)xXrftso%US5VY5hl1JP9EhdL?eXBPqU-!SB ze>-2iSR}LO&tJ_Ki$UdnY;*h_T~Cou?E1ckz3#g^?8M97{R@l7n=)(HE=kwx@~z8G z4$F5eD9a=3WtDH64vB+!xCje6mj!;B{bB(Q;chV;m(CKm7S1KbnIqIssdyZN9h~u}JDL%HBA(*g;Jd>z+JyF4%uJx`$qr( diff --git a/vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv b/vm/stdlib/compiled/latest/stdlib/088_TransferScripts.mv deleted file mode 100644 index dca83ebaca3cf7db7eab5177d516a9c39ff491cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmZ`%JCYMI5LHX99*rcAKbQ@GrJx8lfM}yh3Ag}I1e;9Fj%DmB)^=&cE*yfGnA>mw zat=Vi6=;ny<*h9`c+>s#)ZJ50-yZ#PB!n;^j54qBVy-tA#dr8B%}+I<-|DgdfyGm^?!U`C@emXy&JjLJC=LiuV80!DTspt4NEc*-az-V2z{GGPi^jEuF$ zjGRG3W@OM?ZLPG(_c=rK+Db;IBr2~_4+IySTM4B=R7!&evZVmU19GB(xB^A2!VFf9 z-^O;+FGGCO#q}=r#mlbSe%vH_72`Jc^e%L18)uho>Q>DzgxI9*@V$SRYOc@yv1E2! zG=qM8i@H94aI3yqr&V(mQoCqVd;0$)9z-9)=6zbx<(BunDZ{6ApZcav54#xJ6q`L|f7$Y&VUIyO;lQa{ z3{RnaQ9%vtimqfWpJJ(9AJ*&WCt>&IAP0J^EYN^JGt*-Ult4cWH zp5&17lgn}9QIDiN9$83sm~Ad#>d!Ez&O&ZR7Pwj7OV%WHKB4`Z4r?PR6aJ2r2bNO& E0TGdb)&Kwi diff --git a/vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv b/vm/stdlib/compiled/latest/stdlib/089_TreasuryScripts.mv deleted file mode 100644 index 23b7501a57b223bcba74253c5b6edff659f4d604..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 892 zcmaJ9HS=-r7oa9p60RnOK)NT=!NFWi3(lYi&SomVg-f*O&Io^Y}py3gC z5?+F_o#a4Baq)TP``I6RzCZeHv=AZ$VWqX|a*frBzQP>@ck+h{f70CkqHn+!5K<_C zf^cF%fKtdPv;YW1AbSiI0E!41No!^(2pG{s6F{`a0>X64D$ZsxV?62W`J7QM7Ja?E zSTPcjrhr0MpCU+!vScyi0EnTG||x`B>a5*C$K1Ez+*3N}pE#KA30C^OME* zCm6J}Dz`3c|M6uXN>1-DUk}ppSTD2YAUti|P5s%OiC!J{|9|bcaC|d9o3GQ(RhwsJ zm%5wA@6W7uUAWG-^*PJqtSHW`pmY8q-aR>efWA2T`O6$G#>4yf!()pAXeJC8nFtF5nYYb`&sL&DW3{U{FWx>{EAe19VcD;WIcS9G&&F#Hf$LBEU zO=waaPwJ+L^N^~#ZO1T8eiz%h57kibf}ft=LKVB}weJ0t#<@v{+$7${xlOU!?o*g_ zABKnY$Q=KRe<`Hm_l=_il=QHIf^Y#`G#11_K-xnIIUZw)B>tLI5OabOC7muOIGpGg DJYX;s diff --git a/vm/stdlib/compiled/latest/stdlib/091_U256.mv b/vm/stdlib/compiled/latest/stdlib/091_U256.mv deleted file mode 100644 index e923809b5f9f07f184e1012756e5235bafd57552..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1196 zcmaJ>JCD;q5T1Q_*IwIs-6Qu1i1&enbX=62Qczq&O$BX~Tuja)u}_Y3C-?^lQBqP+ z@;;VUBY87Z!$Fz=cC$Xw?Wd#-=758VAN z8tR>VYQ9Ppe3xHj_{-(cGXiQN0Y;JwBF8c%0onn$mU#+bG{Cpn!1ahi3pERM3k?fR z3oQ?lHLF{bE5jL+OmJNUi6J9kNO&Ze@({7iAjaT;a}DrFzUcN3)w3*1vxRyY9p`B#Mw5v? zPUokiEE1FWto|xX=fiP49Y)C{9wlbwzsaLTtDv(u&u3AY^@`B)pgV9^?64>1%b7_= zd3+WPQM;ljUC}Fo)AYQGX3@N&i{)6PNhFXJ2n+Z+Plt9J`8LW@Igay%8mG9Ab2W>S zX@1g(le5t*o(xCXbUBZb+*`!SbQa}l;+-xRC&TgR_;px`7WpWT@Zhf+&X$BbDMP~j zRx-{wj$63a#X}_7C=-QeIcE}U%M|0|IU&_p)AJF6+~3!_7f2)RTo!guK5|@N+F>fJ zjVv|D$N{Yf+^~uY!;QEjANq`8-hA$hyYhjSTnp}qRS<)Nrm5W0U`t`$uto!>Zk6@# zOXYZ)HQReNa+{K)SF$txADIW4?XSLVsQah1vK)1Kf$-_z=t(a?PXivX5Zw(#$S!X2 zAIE1c44~ao`qXbK?pnS&Cm&5?Jjq~zQh*g=)v)STCc40Dh60Vt0s*eVyHnK*;kGW_ z8r(JOKDKA9@Maf=qrPbNw)7mxnC8u$y+UO_3D&Nw+D_-13RMe(s()~_@7wxi$X?ms QRtcR_xjH6I?s#6r-uCQu!XAdwj2bRQ6Bz{}mxZNMIC_@O}E>4>oDnFN|*3CM6YRty8!PlD$)zGKcTRtSx zA%5Dn%HI1@8d)o=%om$dpWS>WxAI$EX?qiVW#xrBI+fLFS4pdib7?MBYYW|!>iY2k zzPM6)eQt|q%4{AT)M$HNNZ)=ZxdO5@@~V*arfY0e>dT`0=*h=3b*)?5_WDBF_;zlO z2GqKZLZ*;qX;j-%1&1AWo9IScEi3&@Wjojx%W8k*VtZ>rRCkr{dFT61nVVw0xh$-+ zZ`hOQY2PpJvE5i%6`l$F7+KRbr{EXwnx3d-V|ClgsWnQr9n|$T=iUIou0ZeF44us7*L_J~q~A7i*!Z^ig+_6|E?WQA93 z<=%soJQ)iNfZzkXdo%Xw=>a`+`aHp}ba==($NBOkL3>h@S2!ZzX*?$Gtx2gspCA(6 zLQMEABm|x-lyZT%$R`5lY>r6mvmE8ClVk_sp@mAGO?S{a$rpR~#G{l1V&6$pA_$x9 k-|}5Db=CJcJUhT=2l#xq%egybI7Jy+a&b%+y9^}$1C#U(mjD0& diff --git a/vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv b/vm/stdlib/compiled/latest/stdlib/093_YieldFarmingV2.mv deleted file mode 100644 index e0e4f01ae5c0088a7710dfd4eb8c9cce6f995249..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3429 zcmZ`+TW=f36`t$P?#wK;EAt|WvMI}!?WCz%+lfRus*RvX5fm_5z(IjL5gTzOZ_Qhl zOJDNZ0{tC*Y5Q29KnoP;Tl?6z{(wF=f1=;al5_$(1T^RDxzD-GjDElWjR^=LF(sDS z2mJB>sQ44xmVameX6kqHA3^v}lLY@Uf2R7k@k8^sxMBYl|G~!J?Cvyvvv&whN*Lip z5J>_>h_Vb6O2Q?KmQXLD@sLO4@9q+Dmh2Mx2pIhsi#)$|n+o=mBT3}rL{brU zw+>|Q*5T1%)ZIPMz3T@&u!2(~SVSVqLJ|@h5(Gho6oNZ1SXf|~7w`lsDTNwRX95~g zE=6FtV>1?5Pa>driwr`{B^h=kR3f(K(6>60Ma=6lxg=6f16zAU01OeTmALc6U9iyM zLK*7CR2?8BL4rL93E^!bLZ%!>>Uas`_^sr*T%!V5tqEuKb`_T^L6?V^!|H}daIiV= ztMNsVkA^?X%5gEd`qhKr)3ThFbMZ8*o(Ip0t4Ur)pBLkNUS;E%{CxT%pYTs#&(u=E zF5xHhd0wTTW#vmq{xq9qUlyaHdb9h>tjxxFm6uPa!{YMITaJ8IWiRq58x9AV(^U@3 zU(Z4Z7xy3ZI~UpLVlm39eDJ(BJ18c@{PnHxp$}f=#ntm_@O55J55HHYsGisUcHfG; z0&!SouLjw8x|mequz2Ys*velQ^J-qp53(v+eOH|&hNQg6!5JXvGM~>mHlXo07#3xI zQKfHDx3z3CRLiX-t1UU-gNtmYi%C%x*{JwB569W0D(3Sn8T*_KW~&^A=OPCzG1XQ`1OkXX1&XW}6qda`E{N~|`$-~i_Hd|xG8q3$%S8Hs%#xB>`)f#)g#wKfQy2gIJ#6%J; zu@w*rA;UlZ<*)yZR}{kNb)0te{rV_l!4Jd-;)ncL{D}XUaz^hIA{APPH*1TO!FfV%S0J`Ir|-} zj49UB;GB`gI-rY23>4A&{S-qBREBX7W~Fhe^K<3>WoITmT`gmIG*d0UfH(%kTW zB(8x$n^;&A+ajmDnF`C~;fjo#_}e^g8BXm0{*y!#O>tXd1Kfqo++I67sZF~KZv&E~ z@7VB7p%ii3-AwdN5pTrjZSJwq`9pC&CM;T^jOi81AqD|uh}uf88Ja5O-#QX>fV$>M zkU+=9XM^IBVqD(P23>G2POhB< z9~<6hR%xa6scd2inrUDoAgXDiwl&(g^gl}TDjprJ>bELW8GEfx5VJH$Iv412( z%M*)808C3u7tn#m&1}W75q-x+Waz?^sKu_qs6mTu6c&AeN25e?Hd(I4#MTDg_reZq z;t9&+-F+Vs>WjJrh|4#=WygjgguvA#sB41viK~fdvzFV{y#y-6QcX|eCKm1%g5Jjz zur?&5>y&eT+Gw(lQ}CZ|c-W5nY#EJ=xpu*3@zz5A4 zw8|cu#pS9c_dzy_s-Qk}H0WDsp(Al5KUlItu(V^CW(U2bPLp@d?k~WwO=B?cA>zOZ zvXL|so&`^W4YF3gNB~Jo7Osn}JT?+79o2?(w4vh+`A7_Jdqoq~X0%bR3xhMk_Iwk$ zp6al!!vo(?YHN?}FQ=}BP}3Waamctd_Ct2P9=Z3Q8v01s_&b9aKMo!RR@u;EN>OEv zw!=RczRH-k$b?#8?u=2!wCl++ZgLu^e$v$9jyRSNF@8$9ggTlL9n4OGG6JA~{Xl(Y zB21EQ$)CGX<)(qdo8hKm49{31nWPd&IK!nvyR6G`pJ3Q}su%X6uEwpxd!nbis%v_* GNB$2!=DQC7 From 097fd0caa28bcf54bcecb0167da3b9de97d2592d Mon Sep 17 00:00:00 2001 From: sanlee42 Date: Fri, 20 Oct 2023 16:22:57 +0000 Subject: [PATCH 13/17] Add parents to dag --- miner/src/lib.rs | 2 +- rpc/api/src/types.rs | 8 +++--- types/src/block.rs | 62 ++++++++++++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 02887c7f5c..229d371aba 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -251,7 +251,7 @@ impl MinerService { } if let Some(task) = self.current_task.take() { - let tips_header = task.block_template.tips_header.clone(); + let tips_header = task.block_template.parents_hash.clone(); let block = task.finish(nonce, extra); let block_hash: HashValue = block.id(); info!(target: "miner", "Mint new block: {}", block); diff --git a/rpc/api/src/types.rs b/rpc/api/src/types.rs index 532a140998..cfed8fab61 100644 --- a/rpc/api/src/types.rs +++ b/rpc/api/src/types.rs @@ -23,9 +23,7 @@ use starcoin_crypto::{CryptoMaterialError, HashValue, ValidCryptoMaterialStringE use starcoin_resource_viewer::{AnnotatedMoveStruct, AnnotatedMoveValue}; use starcoin_service_registry::ServiceRequest; use starcoin_state_api::{StateProof, StateWithProof, StateWithTableItemProof}; -use starcoin_types::block::{ - Block, BlockBody, BlockHeader, BlockHeaderExtra, BlockInfo, BlockNumber, -}; +use starcoin_types::block::{Block, BlockBody, BlockHeader, BlockHeaderExtra, BlockInfo, BlockNumber, ParentsHash}; use starcoin_types::contract_event::{ContractEvent, ContractEventInfo}; use starcoin_types::event::EventKey; use starcoin_types::genesis_config; @@ -433,6 +431,8 @@ pub struct BlockHeaderView { pub nonce: u32, /// block header extra pub extra: BlockHeaderExtra, + /// block parents + pub parents_hash: ParentsHash, } impl From for BlockHeaderView { @@ -453,6 +453,7 @@ impl From for BlockHeaderView { chain_id: origin.chain_id().id(), nonce: origin.nonce(), extra: *origin.extra(), + parents_hash: origin.parents_hash(), } } } @@ -473,6 +474,7 @@ impl From for BlockHeader { genesis_config::ChainId::new(header_view.chain_id), header_view.nonce, header_view.extra, + header_view.parents_hash, ) } } diff --git a/types/src/block.rs b/types/src/block.rs index 814ca22899..7b6c31ac09 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -21,9 +21,12 @@ use starcoin_vm_types::account_config::genesis_address; use starcoin_vm_types::transaction::authenticator::AuthenticationKey; use std::fmt::Formatter; use std::hash::Hash; + /// Type for block number. pub type BlockNumber = u64; +pub type ParentsHash = Option>; + /// Type for block header extra #[derive(Clone, Default, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, JsonSchema)] pub struct BlockHeaderExtra(#[schemars(with = "String")] [u8; 4]); @@ -48,8 +51,8 @@ impl std::fmt::Display for BlockHeaderExtra { impl<'de> Deserialize<'de> for BlockHeaderExtra { fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { if deserializer.is_human_readable() { let s = ::deserialize(deserializer)?; @@ -76,8 +79,8 @@ impl<'de> Deserialize<'de> for BlockHeaderExtra { impl Serialize for BlockHeaderExtra { fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, + where + S: Serializer, { if serializer.is_human_readable() { format!("0x{}", hex::encode(self.0)).serialize(serializer) @@ -88,7 +91,7 @@ impl Serialize for BlockHeaderExtra { } #[derive( - Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, JsonSchema, +Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, JsonSchema, )] pub struct BlockIdAndNumber { pub id: HashValue, @@ -153,6 +156,10 @@ pub struct BlockHeader { nonce: u32, /// block header extra extra: BlockHeaderExtra, + /// Parents hash. + #[serde(skip_serializing_if = "Option::is_none")] + parents_hash: ParentsHash, + } impl BlockHeader { @@ -170,6 +177,7 @@ impl BlockHeader { chain_id: ChainId, nonce: u32, extra: BlockHeaderExtra, + parents_hash: ParentsHash, ) -> BlockHeader { Self::new_with_auth_key( parent_hash, @@ -186,6 +194,7 @@ impl BlockHeader { chain_id, nonce, extra, + parents_hash, ) } @@ -205,6 +214,7 @@ impl BlockHeader { chain_id: ChainId, nonce: u32, extra: BlockHeaderExtra, + parents_hash: ParentsHash, ) -> BlockHeader { let mut header = BlockHeader { id: None, @@ -222,6 +232,7 @@ impl BlockHeader { body_hash, chain_id, extra, + parents_hash, }; header.id = Some(header.crypto_hash()); header @@ -248,6 +259,9 @@ impl BlockHeader { self.parent_hash } + pub fn parents_hash(&self) -> ParentsHash { + self.parents_hash.clone() + } pub fn timestamp(&self) -> u64 { self.timestamp } @@ -327,6 +341,7 @@ impl BlockHeader { chain_id, 0, BlockHeaderExtra::default(), + None, ) } @@ -345,6 +360,7 @@ impl BlockHeader { ChainId::test(), 0, BlockHeaderExtra([0u8; 4]), + None, ) } @@ -355,8 +371,8 @@ impl BlockHeader { impl<'de> Deserialize<'de> for BlockHeader { fn deserialize(deserializer: D) -> Result>::Error> - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(rename = "BlockHeader")] @@ -375,6 +391,7 @@ impl<'de> Deserialize<'de> for BlockHeader { chain_id: ChainId, nonce: u32, extra: BlockHeaderExtra, + parents_hash:ParentsHash, } let header_data = BlockHeaderData::deserialize(deserializer)?; @@ -393,6 +410,7 @@ impl<'de> Deserialize<'de> for BlockHeader { header_data.chain_id, header_data.nonce, header_data.extra, + header_data.parents_hash ); Ok(block_header) } @@ -414,6 +432,7 @@ impl Default for BlockHeader { ChainId::test(), 0, BlockHeaderExtra([0u8; 4]), + None, ) } } @@ -434,6 +453,7 @@ impl Sample for BlockHeader { ChainId::test(), 0, BlockHeaderExtra([0u8; 4]), + None, ) } } @@ -454,6 +474,7 @@ impl Into for BlockHeader { difficulty: self.difficulty, body_hash: self.body_hash, chain_id: self.chain_id, + parents_hash: self.parents_hash, } } } @@ -485,6 +506,8 @@ pub struct RawBlockHeader { pub body_hash: HashValue, /// The chain id pub chain_id: ChainId, + /// parents hash + pub parents_hash:ParentsHash, } #[derive(Default)] @@ -587,7 +610,7 @@ impl BlockHeaderBuilder { } #[derive( - Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, +Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, )] pub struct BlockBody { /// The transactions in this block. @@ -657,8 +680,8 @@ pub struct Block { impl Block { pub fn new(header: BlockHeader, body: B) -> Self - where - B: Into, + where + B: Into, { Block { header, @@ -774,7 +797,7 @@ impl Sample for Block { /// `BlockInfo` is the object we store in the storage. It consists of the /// block as well as the execution result of this block. #[derive( - Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, JsonSchema, +Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, JsonSchema, )] pub struct BlockInfo { /// Block id @@ -863,8 +886,8 @@ pub struct BlockTemplate { pub difficulty: U256, /// Block consensus strategy pub strategy: ConsensusStrategy, - /// Tips - pub tips_header: Option>, + /// parents + pub parents_hash: ParentsHash, } impl BlockTemplate { @@ -878,7 +901,7 @@ impl BlockTemplate { difficulty: U256, strategy: ConsensusStrategy, block_metadata: BlockMetadata, - current_tips: Option>, + parents_hash: ParentsHash, ) -> Self { let (parent_hash, timestamp, author, _author_auth_key, _, number, _, _) = block_metadata.into_inner(); @@ -896,7 +919,7 @@ impl BlockTemplate { chain_id, difficulty, strategy, - tips_header: current_tips, + parents_hash, } } @@ -915,6 +938,7 @@ impl BlockTemplate { self.chain_id, nonce, extra, + self.parents_hash, ); Block { header, @@ -937,6 +961,7 @@ impl BlockTemplate { self.chain_id, nonce, extra, + None, ); Block { header, @@ -945,10 +970,10 @@ impl BlockTemplate { } fn generate_parent_header(&self) -> HashValue { - if self.tips_header.is_none() { + if self.parents_hash.is_none() { return self.parent_hash; } - let mut tips = self.tips_header.as_ref().unwrap().clone(); + let mut tips = self.parents_hash.as_ref().unwrap().clone(); tips.sort(); HashValue::sha3_256_of(&tips.encode().expect("dag parent must encode successfully")) } @@ -967,6 +992,7 @@ impl BlockTemplate { body_hash: self.body_hash, difficulty: self.difficulty, chain_id: self.chain_id, + parents_hash: self.parents_hash.clone(), } } @@ -984,6 +1010,7 @@ impl BlockTemplate { body_hash: self.body_hash, difficulty: self.difficulty, chain_id: self.chain_id, + parents_hash: self.parents_hash.clone() } } @@ -1030,6 +1057,7 @@ impl BlockTemplate { self.chain_id, nonce, extra, + self.parents_hash ) } } From 8ac333914398d96b1c01f1149b1b0a2f2789582f Mon Sep 17 00:00:00 2001 From: Jack Huang Date: Mon, 6 Nov 2023 11:15:09 +0800 Subject: [PATCH 14/17] add sync for flexidag (#3984) --- Cargo.lock | 26 ++ Cargo.toml | 3 + account/src/account_test.rs | 2 +- benchmarks/src/chain.rs | 2 +- chain/Cargo.toml | 1 + chain/api/Cargo.toml | 1 - chain/api/src/chain.rs | 17 +- chain/api/src/service.rs | 6 +- chain/mock/src/mock_chain.rs | 6 +- chain/service/Cargo.toml | 2 + chain/service/src/chain_service.rs | 124 +++---- chain/src/chain.rs | 216 +---------- chain/src/verifier/mod.rs | 53 +-- chain/tests/block_test_utils.rs | 3 +- chain/tests/test_block_chain.rs | 20 +- consensus/Cargo.toml | 1 + consensus/src/consensus_test.rs | 1 + consensus/src/dag/blockdag.rs | 83 ++++- flexidag/Cargo.toml | 28 ++ flexidag/src/flexidag_service.rs | 334 ++++++++++++++++++ flexidag/src/lib.rs | 17 + miner/Cargo.toml | 2 + miner/src/create_block_template/mod.rs | 18 +- network-rpc/api/src/lib.rs | 2 +- network/api/src/peer_provider.rs | 8 + network/src/service.rs | 4 +- network/types/src/peer_info.rs | 2 + node/src/node.rs | 14 +- storage/src/flexi_dag/mod.rs | 1 + storage/src/lib.rs | 107 ++---- storage/src/tests/test_block.rs | 4 + storage/src/tests/test_storage.rs | 5 +- sync/Cargo.toml | 1 + sync/api/src/lib.rs | 8 +- .../block_connector_service.rs | 32 +- sync/src/block_connector/mod.rs | 2 - .../src/block_connector/test_illegal_block.rs | 8 +- .../block_connector/test_write_block_chain.rs | 6 +- .../test_write_dag_block_chain.rs | 3 +- sync/src/block_connector/write_block_chain.rs | 166 +++++---- sync/src/sync.rs | 55 +-- sync/src/tasks/block_sync_task.rs | 105 +++--- sync/src/tasks/mod.rs | 21 +- sync/src/tasks/sync_dag_block_task.rs | 31 +- sync/src/tasks/sync_dag_full_task.rs | 42 ++- sync/src/verified_rpc_client.rs | 4 +- types/src/block.rs | 8 +- types/src/header.rs | 10 +- types/src/startup_info.rs | 40 ++- types/src/system_events.rs | 2 +- 50 files changed, 973 insertions(+), 684 deletions(-) create mode 100644 flexidag/Cargo.toml create mode 100644 flexidag/src/flexidag_service.rs create mode 100644 flexidag/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1afc771ef7..cfd552cd93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9265,6 +9265,7 @@ name = "starcoin-chain" version = "1.13.7" dependencies = [ "anyhow", + "async-std", "bcs-ext", "clap 3.2.23", "proptest", @@ -9371,6 +9372,7 @@ name = "starcoin-chain-service" version = "1.13.7" dependencies = [ "anyhow", + "async-std", "async-trait", "futures 0.3.26", "rand 0.8.5", @@ -9382,6 +9384,7 @@ dependencies = [ "starcoin-config", "starcoin-consensus", "starcoin-crypto", + "starcoin-flexidag", "starcoin-logger", "starcoin-network-rpc-api", "starcoin-service-registry", @@ -9516,6 +9519,7 @@ dependencies = [ "rust-argon2", "serde 1.0.152", "sha3", + "starcoin-accumulator", "starcoin-chain-api", "starcoin-config", "starcoin-crypto", @@ -9693,6 +9697,25 @@ dependencies = [ "tokio-executor 0.2.0-alpha.6", ] +[[package]] +name = "starcoin-flexidag" +version = "1.13.7" +dependencies = [ + "anyhow", + "async-trait", + "futures 0.3.26", + "starcoin-accumulator", + "starcoin-config", + "starcoin-consensus", + "starcoin-crypto", + "starcoin-logger", + "starcoin-service-registry", + "starcoin-storage", + "starcoin-types", + "thiserror", + "tokio", +] + [[package]] name = "starcoin-framework" version = "11.0.0" @@ -9883,6 +9906,7 @@ name = "starcoin-miner" version = "1.13.7" dependencies = [ "anyhow", + "async-std", "bcs-ext", "futures 0.3.26", "futures-timer", @@ -9898,6 +9922,7 @@ dependencies = [ "starcoin-consensus", "starcoin-crypto", "starcoin-executor", + "starcoin-flexidag", "starcoin-genesis", "starcoin-logger", "starcoin-metrics", @@ -10745,6 +10770,7 @@ dependencies = [ "starcoin-consensus", "starcoin-crypto", "starcoin-executor", + "starcoin-flexidag", "starcoin-genesis", "starcoin-logger", "starcoin-metrics", diff --git a/Cargo.toml b/Cargo.toml index 2bc809a2d2..c1e537d11b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ members = [ "cmd/miner_client/api", "cmd/db-exporter", "cmd/genesis-nft-miner", + "flexidag", ] default-members = [ @@ -217,6 +218,7 @@ default-members = [ "stratum", "cmd/miner_client/api", "cmd/db-exporter", + "flexidag", ] [profile.dev] @@ -499,6 +501,7 @@ starcoin-parallel-executor = { path = "vm/parallel-executor" } starcoin-transaction-benchmarks = { path = "vm/transaction-benchmarks" } starcoin-language-e2e-tests = { path = "vm/e2e-tests" } starcoin-proptest-helpers = { path = "vm/proptest-helpers" } +starcoin-flexidag = { path = "flexidag" } syn = { version = "1.0.107", features = [ "full", diff --git a/account/src/account_test.rs b/account/src/account_test.rs index bba50ab6cb..5e36ea2528 100644 --- a/account/src/account_test.rs +++ b/account/src/account_test.rs @@ -224,7 +224,7 @@ pub fn test_wallet_account() -> Result<()> { ); //println!("verify result is {:?}", sign.verify(&raw_txn, &public_key)?); println!("public key is {:?}", public_key.to_bytes().as_ref()); - println!("hash value is {:?}", hash_value.as_ref()); + println!("hash value is {:?}", hash_value); println!("key is {:?}", key.derived_address()); println!("address is {:?},result is {:?}", address, result); diff --git a/benchmarks/src/chain.rs b/benchmarks/src/chain.rs index c7f9e3344a..da52a5043d 100644 --- a/benchmarks/src/chain.rs +++ b/benchmarks/src/chain.rs @@ -77,7 +77,7 @@ impl ChainBencher { let block = ConsensusStrategy::Dummy .create_block(block_template, self.net.time_service().as_ref()) .unwrap(); - self.chain.write().apply(block, None).unwrap(); + self.chain.write().apply(block).unwrap(); } } diff --git a/chain/Cargo.toml b/chain/Cargo.toml index 8dac82c310..5a01426439 100644 --- a/chain/Cargo.toml +++ b/chain/Cargo.toml @@ -24,6 +24,7 @@ starcoin-vm-types = { workspace = true } starcoin-storage = { workspace = true } thiserror = { workspace = true } starcoin-network-rpc-api = { workspace = true } +async-std = { workspace = true } [dev-dependencies] proptest = { workspace = true } diff --git a/chain/api/Cargo.toml b/chain/api/Cargo.toml index 1648fcdee5..094c6edcb8 100644 --- a/chain/api/Cargo.toml +++ b/chain/api/Cargo.toml @@ -18,7 +18,6 @@ thiserror = { workspace = true } starcoin-network-rpc-api = { workspace = true } starcoin-config = { workspace = true } - [dev-dependencies] [features] diff --git a/chain/api/src/chain.rs b/chain/api/src/chain.rs index 4f25192e6b..8e872f5315 100644 --- a/chain/api/src/chain.rs +++ b/chain/api/src/chain.rs @@ -85,7 +85,7 @@ pub trait ChainReader { fn execute( &self, block: VerifiedBlock, - parents_hash: Option>, + transaction_parent: Option, ) -> Result; /// Get chain transaction infos fn get_transaction_infos( @@ -107,8 +107,6 @@ pub trait ChainReader { access_path: Option, ) -> Result>; - /// get the current tips hash value - fn current_tips_hash(&self) -> Option; fn net_id(&self) -> ChainNetworkID; } @@ -121,22 +119,9 @@ pub trait ChainWriter { fn apply( &mut self, block: Block, - parents_hash: Option>, ) -> Result; fn chain_state(&mut self) -> &ChainStateDB; - - /// Get the dag accumulator info - fn get_current_dag_accumulator_info(&self) -> Result; - - /// Fork the accumulator - fn fork_dag_accumulator(&mut self, accumulator_info: AccumulatorInfo) -> Result<()>; - - /// Append the dag accumulator leaf - fn append_dag_accumulator_leaf( - &mut self, - tips: Vec, - ) -> Result<(HashValue, AccumulatorInfo)>; } /// `Chain` is a trait that defines a single Chain. diff --git a/chain/api/src/service.rs b/chain/api/src/service.rs index 2610b8862c..c2e3de8a2f 100644 --- a/chain/api/src/service.rs +++ b/chain/api/src/service.rs @@ -1,6 +1,8 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2 +use std::sync::{Arc, Mutex}; + use crate::message::{ChainRequest, ChainResponse}; use crate::TransactionInfoWithProof; use anyhow::{bail, Result}; @@ -9,7 +11,7 @@ use starcoin_network_rpc_api::dag_protocol::{ self, GetDagAccumulatorLeaves, GetTargetDagAccumulatorLeafDetail, TargetDagAccumulatorLeaf, TargetDagAccumulatorLeafDetail, }; -use starcoin_service_registry::{ActorService, ServiceHandler, ServiceRef}; +use starcoin_service_registry::{ActorService, ServiceContext, ServiceHandler, ServiceRef}; use starcoin_types::contract_event::{ContractEvent, ContractEventInfo}; use starcoin_types::filter::Filter; use starcoin_types::startup_info::ChainStatus; @@ -91,7 +93,7 @@ pub trait ReadableChainService { /// Writeable block chain service trait pub trait WriteableChainService: Send + Sync { - fn try_connect(&mut self, block: Block, tips_header: Option>) -> Result<()>; + fn try_connect(&mut self, block: Block) -> Result<()>; } #[async_trait::async_trait] diff --git a/chain/mock/src/mock_chain.rs b/chain/mock/src/mock_chain.rs index 6259bb4a3b..dbfe797320 100644 --- a/chain/mock/src/mock_chain.rs +++ b/chain/mock/src/mock_chain.rs @@ -134,15 +134,15 @@ impl MockChain { .create_block(template, self.net.time_service().as_ref()) } - pub fn apply(&mut self, block: Block, parents_hash: Option>) -> Result<()> { - self.head.apply(block, parents_hash)?; + pub fn apply(&mut self, block: Block) -> Result<()> { + self.head.apply(block, None)?; Ok(()) } pub fn produce_and_apply(&mut self) -> Result { let block = self.produce()?; let header = block.header().clone(); - self.apply(block, None)?; + self.apply(block)?; Ok(header) } diff --git a/chain/service/Cargo.toml b/chain/service/Cargo.toml index e1dbd7f95d..b772b9e707 100644 --- a/chain/service/Cargo.toml +++ b/chain/service/Cargo.toml @@ -1,5 +1,6 @@ [dependencies] anyhow = { workspace = true } +async-std = { workspace = true } async-trait = { workspace = true } futures = { workspace = true } rand = { workspace = true } @@ -21,6 +22,7 @@ tokio = { workspace = true } starcoin-network-rpc-api = { workspace = true } starcoin-consensus = { workspace = true } starcoin-accumulator = { package = "starcoin-accumulator", workspace = true } +starcoin-flexidag = { workspace = true } [dev-dependencies] stest = { workspace = true } diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index d00b0ba5bb..1b61e0c4ec 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -1,7 +1,7 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::{bail, format_err, Error, Result}; +use anyhow::{bail, format_err, Error, Result, Ok}; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::BlockChain; @@ -12,13 +12,15 @@ use starcoin_chain_api::{ use starcoin_config::NodeConfig; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; +use starcoin_flexidag::{FlexidagService, flexidag_service}; +use starcoin_flexidag::flexidag_service::{GetDagAccumulatorLeafDetail, UpdateDagTips, GetDagBlockParents}; use starcoin_logger::prelude::*; use starcoin_network_rpc_api::dag_protocol::{ GetDagAccumulatorLeaves, GetTargetDagAccumulatorLeafDetail, TargetDagAccumulatorLeaf, TargetDagAccumulatorLeafDetail, }; use starcoin_service_registry::{ - ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, + ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRef, }; use starcoin_storage::{BlockStore, Storage, Store}; use starcoin_types::block::ExecutedBlock; @@ -46,7 +48,7 @@ impl ChainReaderService { config: Arc, startup_info: StartupInfo, storage: Arc, - dag: Arc>, + flexidag_service: ServiceRef, vm_metrics: Option, ) -> Result { Ok(Self { @@ -54,7 +56,7 @@ impl ChainReaderService { config.clone(), startup_info, storage.clone(), - dag.clone(), + flexidag_service, vm_metrics.clone(), )?, }) @@ -69,10 +71,8 @@ impl ServiceFactory for ChainReaderService { .get_startup_info()? .ok_or_else(|| format_err!("StartupInfo should exist at service init."))?; let vm_metrics = ctx.get_shared_opt::()?; - let dag = ctx - .get_shared_opt::>>()? - .expect("dag should be initialized at service init"); - Self::new(config, startup_info, storage, dag.clone(), vm_metrics) + let flexidag_service = ctx.service_ref::()?.clone(); + Self::new(config, startup_info, storage, flexidag_service, vm_metrics) } } @@ -89,11 +89,12 @@ impl ActorService for ChainReaderService { } impl EventHandler for ChainReaderService { - fn handle_event(&mut self, event: NewHeadBlock, _ctx: &mut ServiceContext) { - let new_head = event.0.block().header(); + fn handle_event(&mut self, event: NewHeadBlock, ctx: &mut ServiceContext) { + let new_head = event.0.block().header().clone(); if let Err(e) = if self.inner.get_main().can_connect(event.0.as_ref()) { match self.inner.update_chain_head(event.0.as_ref().clone()) { - Ok(_) => self.inner.update_dag_accumulator(new_head.id()), + // wait for fixing: update_dag_accumulator should be in BlockChain + std::result::Result::Ok(_) => self.inner.update_dag_accumulator(new_head), Err(e) => Err(e), } } else { @@ -108,7 +109,7 @@ impl ServiceHandler for ChainReaderService { fn handle( &mut self, msg: ChainRequest, - _ctx: &mut ServiceContext, + ctx: &mut ServiceContext, ) -> Result { match msg { ChainRequest::CurrentHeader() => Ok(ChainResponse::BlockHeader(Box::new( @@ -265,14 +266,12 @@ impl ServiceHandler for ChainReaderService { ChainRequest::GetTargetDagAccumulatorLeafDetail { leaf_index, batch_size, - } => Ok(ChainResponse::TargetDagAccumulatorLeafDetail( - self.inner.get_target_dag_accumulator_leaf_detail( - GetTargetDagAccumulatorLeafDetail { - leaf_index, - batch_size, - }, - )?, - )), + } => { + Ok(ChainResponse::TargetDagAccumulatorLeafDetail(self.inner.get_target_dag_accumulator_leaf_detail(GetTargetDagAccumulatorLeafDetail { + leaf_index, + batch_size, + })?)) + }, } } } @@ -282,8 +281,8 @@ pub struct ChainReaderServiceInner { startup_info: StartupInfo, main: BlockChain, storage: Arc, + flexidag_service: ServiceRef, vm_metrics: Option, - dag: Arc>, } impl ChainReaderServiceInner { @@ -291,7 +290,7 @@ impl ChainReaderServiceInner { config: Arc, startup_info: StartupInfo, storage: Arc, - dag: Arc>, + flexidag_service: ServiceRef, vm_metrics: Option, ) -> Result { let net = config.net(); @@ -307,7 +306,7 @@ impl ChainReaderServiceInner { startup_info, main, storage, - dag: dag.clone(), + flexidag_service, vm_metrics, }) } @@ -333,8 +332,10 @@ impl ChainReaderServiceInner { Ok(()) } - pub fn update_dag_accumulator(&mut self, head_id: HashValue) -> Result<()> { - self.main.update_dag_accumulator(head_id) + pub fn update_dag_accumulator(&mut self, new_block_header: BlockHeader) -> Result<()> { + async_std::task::block_on(self.flexidag_service.send(UpdateDagTips { + block_header: new_block_header, + }))? } } @@ -356,17 +357,16 @@ impl ReadableChainService for ChainReaderServiceInner { .into_iter() .map(|block| { if let Some(block) = block { - let parents = match self - .dag - .lock() - .expect("failed to lock dag") - .get_parents(block.id()) + let result_parents = async_std::task::block_on(self.flexidag_service.send(GetDagBlockParents { + block_id: block.id(), + })).expect("failed to get the dag block parents"); + let parents = match result_parents { - Ok(parents) => parents, + std::result::Result::Ok(parents) => parents.parents, Err(_) => panic!("failed to get parents of block {}", block.id()), }; let transaction_parent = match self.storage.get_block_info(block.id()) { - Ok(block_info) => { + std::result::Result::Ok(block_info) => { if let Some(block_info) = &block_info { let block_accumulator = MerkleAccumulator::new_with_info( block_info.block_accumulator_info.clone(), @@ -510,54 +510,32 @@ impl ReadableChainService for ChainReaderServiceInner { &self, req: GetDagAccumulatorLeaves, ) -> anyhow::Result> { - match self - .main - .get_dag_leaves(req.accumulator_leaf_index, true, req.batch_size) - { - Ok(leaves) => Ok(leaves - .into_iter() - .enumerate() - .map( - |(index, leaf)| match self.main.get_dag_accumulator_snapshot(leaf) { - Ok(snapshot) => TargetDagAccumulatorLeaf { - accumulator_root: snapshot.accumulator_info.accumulator_root, - leaf_index: req.accumulator_leaf_index.saturating_sub(index as u64), - }, - Err(error) => { - panic!( - "error occured when query the accumulator snapshot: {}", - error.to_string() - ); - } - }, - ) - .collect()), - Err(error) => { - bail!( - "an error occured when getting the leaves of the accumulator, {}", - error.to_string() - ); + Ok(async_std::task::block_on(self.flexidag_service.send(flexidag_service::GetDagAccumulatorLeaves { + leaf_index: req.accumulator_leaf_index, + batch_size: req.batch_size, + reverse: true, + }))??.into_iter().map(|leaf| { + TargetDagAccumulatorLeaf { + accumulator_root: leaf.dag_accumulator_root, + leaf_index: leaf.leaf_index, } - } + }).collect()) } fn get_target_dag_accumulator_leaf_detail( &self, req: GetTargetDagAccumulatorLeafDetail, ) -> anyhow::Result> { - let end_index = std::cmp::min( - req.leaf_index + req.batch_size - 1, - self.main.get_dag_current_leaf_number()? - 1, - ); - let mut details = [].to_vec(); - for index in req.leaf_index..=end_index { - let snapshot = self.main.get_dag_accumulator_snapshot_by_index(index)?; - details.push(TargetDagAccumulatorLeafDetail { - accumulator_root: snapshot.accumulator_info.accumulator_root, - tips: snapshot.child_hashes, - }); - } - Ok(details) + let dag_details = async_std::task::block_on(self.flexidag_service.send(GetDagAccumulatorLeafDetail { + leaf_index: req.leaf_index, + batch_size: req.batch_size, + }))??; + Ok(dag_details.into_iter().map(|detail| { + TargetDagAccumulatorLeafDetail { + accumulator_root: detail.accumulator_root, + tips: detail.tips, + } + }).collect()) } } diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 43247700d1..59078ce555 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -43,6 +43,7 @@ use starcoin_types::{ }; use starcoin_vm_types::access_path::AccessPath; use starcoin_vm_types::account_config::genesis_address; +use starcoin_vm_types::effects::Op; use starcoin_vm_types::genesis_config::ConsensusStrategy; use starcoin_vm_types::on_chain_resource::Epoch; use std::cmp::min; @@ -66,7 +67,7 @@ pub struct BlockChain { uncles: HashMap, epoch: Epoch, vm_metrics: Option, - dag_accumulator: Option, + // dag_accumulator: Option, net: ChainNetworkID, } @@ -84,28 +85,6 @@ impl BlockChain { Self::new_with_uncles(time_service, head, None, storage, net, vm_metrics) } - fn get_dag_data( - storage: Arc, - header: &BlockHeader, - net: ChainNetworkID, - ) -> Result<(Option, Option>)> { - let (op_dag_accumulator_info, op_tips) = storage.get_flexidag_init_data(header, net)?; - match (op_dag_accumulator_info, op_tips.clone()) { - (Some(dag_accumulator_info), Some(_tips)) => { - let dag_accumulator = Some(info_2_accumulator( - dag_accumulator_info, - AccumulatorStoreType::SyncDag, - storage.as_ref(), - )); - Ok((dag_accumulator, op_tips)) - } - (None, None) => Ok((None, None)), - _ => { - bail!("dag accumulator info and tips should be both None or Some") - } - } - } - fn new_with_uncles( time_service: Arc, head_block: Block, @@ -141,8 +120,6 @@ impl BlockChain { // .get_accumulator_snapshot_storage() // .get(head_id)? // .map(|snapshot| snapshot.child_hashes); - let (dag_accumulator, dag_snapshot_tips) = - Self::get_dag_data(storage.clone(), head_block.header(), net.clone())?; let mut chain = Self { genesis_hash: genesis, time_service, @@ -157,7 +134,7 @@ impl BlockChain { storage.as_ref(), ), status: ChainStatusWithBlock { - status: ChainStatus::new(head_block.header.clone(), block_info, dag_snapshot_tips), + status: ChainStatus::new(head_block.header.clone(), block_info), head: head_block, }, statedb: chain_state, @@ -165,7 +142,6 @@ impl BlockChain { uncles: HashMap::new(), epoch, vm_metrics, - dag_accumulator, net, }; watch(CHAIN_WATCH_NAME, "n1251"); @@ -338,7 +314,8 @@ impl BlockChain { difficulty, strategy, None, - self.status.status.tips_hash.clone(), + // jacktest: TODO: this create single chian block not a dag block + None, )?; let excluded_txns = opened_block.push_txns(user_txns)?; let template = opened_block.finalize()?; @@ -404,14 +381,13 @@ impl BlockChain { pub fn apply_with_verifier( &mut self, block: Block, - parents_hash: Option>, ) -> Result where V: BlockVerifier, { let verified_block = self.verify_with_verifier::(block)?; watch(CHAIN_WATCH_NAME, "n1"); - let executed_block = self.execute(verified_block, parents_hash)?; + let executed_block = self.execute(verified_block, transaction_parent)?; watch(CHAIN_WATCH_NAME, "n2"); self.connect(executed_block) } @@ -420,7 +396,6 @@ impl BlockChain { pub fn update_chain_head( &mut self, block: Block, - parents_hash: Option>, ) -> Result { let block_info = self .storage @@ -429,7 +404,6 @@ impl BlockChain { self.connect(ExecutedBlock { block, block_info, - parents_hash, }) } @@ -442,8 +416,8 @@ impl BlockChain { epoch: &Epoch, parent_status: Option, block: Block, + transaction_parent: Option, vm_metrics: Option, - parents_hash: Option>, ) -> Result { let header = block.header(); debug_assert!(header.is_genesis() || parent_status.is_some()); @@ -601,7 +575,6 @@ impl BlockChain { Ok(ExecutedBlock { block, block_info, - parents_hash, }) } @@ -612,83 +585,6 @@ impl BlockChain { pub fn get_block_accumulator(&self) -> &MerkleAccumulator { &self.block_accumulator } - - pub fn get_dag_leaves( - &self, - start_index: u64, - reverse: bool, - batch_size: u64, - ) -> Result> { - self.dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .get_leaves(start_index, reverse, batch_size) - } - - pub fn get_dag_current_leaf_number(&self) -> Result { - Ok(self - .dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .num_leaves()) - } - - pub fn get_dag_accumulator_snapshot(&self, key: HashValue) -> Result { - Ok(self - .storage - .query_by_hash(key)? - .expect("dag accumualator's snapshot should not be None")) - } - - pub fn get_dag_accumulator_snapshot_by_index( - &self, - leaf_index: u64, - ) -> Result { - let leaf_hash = self - .dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .get_leaf(leaf_index)? - .expect("dag accumulator's leaf should not be None"); - Ok(self - .storage - .query_by_hash(leaf_hash)? - .expect("dag accumualator's snapshot should not be None")) - } - - pub fn update_dag_accumulator(&mut self, head_id: HashValue) -> Result<()> { - self.dag_accumulator = Some( - self.dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .fork( - self.storage - .get_dag_accumulator_info(head_id) - .expect("accumulator info should not be None"), - ), - ); - Ok(()) - } - - pub fn dag_parents_in_tips(&self, dag_parents: Vec) -> Result { - Ok(dag_parents - .into_iter() - .all(|parent| match &self.status.status.tips_hash { - Some(tips) => tips.contains(&parent), - None => false, - })) - } - - pub fn is_head_of_dag_accumulator(&self, next_tips: Vec) -> Result { - let key = Self::calculate_dag_accumulator_key(next_tips)?; - let next_tips_info = self.storage.get_dag_accumulator_info(key)?; - - return Ok(next_tips_info - == self - .dag_accumulator - .as_ref() - .map(|accumulator| accumulator.get_info())); - } } impl ChainReader for BlockChain { @@ -698,7 +594,7 @@ impl ChainReader for BlockChain { self.genesis_hash, self.status.status.clone(), self.storage - .get_dag_accumulator_info(self.status.head.header().id()) + .get_dag_accumulator_info() .expect(&format!( "the dag accumulator info cannot be found by id: {}", self.status.head.header().id() @@ -714,7 +610,6 @@ impl ChainReader for BlockChain { ExecutedBlock::new( self.status.head.clone(), self.status.status.info.clone(), - self.status.status.tips_hash.clone(), ) } @@ -722,19 +617,6 @@ impl ChainReader for BlockChain { self.status.status.head().clone() } - fn current_tips_hash(&self) -> Option { - match self.status.status.tips_hash.clone() { - Some(tips_hash) => { - assert!(!tips_hash.is_empty()); - Some( - Self::calculate_dag_accumulator_key(tips_hash) - .expect("calculate dag key should be successful"), - ) - } - None => None, - } - } - fn net_id(&self) -> ChainNetworkID { self.net.clone() } @@ -954,7 +836,7 @@ impl ChainReader for BlockChain { fn execute( &self, verified_block: VerifiedBlock, - parents_hash: Option>, + transaction_parent: Option, ) -> Result { Self::execute_block_and_save( self.storage.as_ref(), @@ -964,8 +846,8 @@ impl ChainReader for BlockChain { &self.epoch, Some(self.status.status.clone()), verified_block.0, + transaction_parent, self.vm_metrics.clone(), - parents_hash, ) } @@ -1176,30 +1058,13 @@ impl ChainWriter for BlockChain { if executed_block.block.header().parent_hash() == self.status.status.head().id() { return true; } else { - return Self::calculate_dag_accumulator_key( - self.status - .status - .tips_hash - .as_ref() - .expect("dag blocks must have tips") - .clone(), - ) - .expect("failed to calculate the tips hash") - == executed_block.block().header().parent_hash(); + // jacktest: TODO: check if the parents is in the dag? + return true; } } fn connect(&mut self, executed_block: ExecutedBlock) -> Result { let (block, block_info) = (executed_block.block(), executed_block.block_info()); - if self.status.status.tips_hash.is_some() { - let mut tips = self.status.status.tips_hash.clone().unwrap(); - tips.sort(); - debug_assert!( - block.header().parent_hash() == Self::calculate_dag_accumulator_key(tips.clone())? - || block.header().parent_hash() == self.status.status.head().id() - ); - } else { - debug_assert!(block.header().parent_hash() == self.status.status.head().id()); - } + debug_assert!(block.header().parent_hash() == self.status.status.head().id()); //TODO try reuse accumulator and state db. let txn_accumulator_info = block_info.get_txn_accumulator_info(); let block_accumulator_info = block_info.get_block_accumulator_info(); @@ -1216,18 +1081,11 @@ impl ChainWriter for BlockChain { ); self.statedb = ChainStateDB::new(self.storage.clone().into_super_arc(), Some(state_root)); - let tips = self.status.status.tips_hash.clone(); - let next_tips = match tips { - Some(mut tips) => { - if !tips.contains(&block.header().id()) { - tips.push(block.header().id()); - } - Some(tips) - } - None => None, - }; self.status = ChainStatusWithBlock { - status: ChainStatus::new(block.header().clone(), block_info.clone(), next_tips), + status: ChainStatus::new( + block.header().clone(), + block_info.clone(), + ), head: block.clone(), }; if self.epoch.end_block_number() == block.header().number() { @@ -1245,51 +1103,13 @@ impl ChainWriter for BlockChain { fn apply( &mut self, block: Block, - parents_hash: Option>, ) -> Result { - self.apply_with_verifier::(block, parents_hash) + self.apply_with_verifier::(block) } fn chain_state(&mut self) -> &ChainStateDB { &self.statedb } - - fn get_current_dag_accumulator_info(&self) -> Result { - Ok(self - .dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumualator is None"))? - .get_info()) - } - - fn fork_dag_accumulator(&mut self, accumulator_info: AccumulatorInfo) -> Result<()> { - self.dag_accumulator = Some( - self.dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .fork(Some(accumulator_info)), - ); - Ok(()) - } - - fn append_dag_accumulator_leaf( - &mut self, - tips: Vec, - ) -> Result<(HashValue, AccumulatorInfo)> { - let key = Self::calculate_dag_accumulator_key(tips.clone())?; - let dag_accumulator = self - .dag_accumulator - .as_ref() - .ok_or_else(|| anyhow!("dag accumulator is None"))? - .fork(None); - dag_accumulator.append(&[key])?; - dag_accumulator.flush()?; - self.storage - .append_dag_accumulator_leaf(key, tips, dag_accumulator.get_info())?; - let accumulator_info = dag_accumulator.get_info(); - self.dag_accumulator = Some(dag_accumulator); - Ok((key, accumulator_info)) - } } pub(crate) fn info_2_accumulator( diff --git a/chain/src/verifier/mod.rs b/chain/src/verifier/mod.rs index 1d7d887f31..e398bee747 100644 --- a/chain/src/verifier/mod.rs +++ b/chain/src/verifier/mod.rs @@ -182,33 +182,34 @@ impl BlockVerifier for BasicVerifier { let expect_number = current.number().saturating_add(1); // dag - if chain_status.tips_hash.is_some() { - let mut tips_hash = chain_status.tips_hash.clone().unwrap(); - tips_hash.sort(); + // jacktest: TODO: the verifying should be modified!!! + // if chain_status.tips_hash.is_some() { + // let mut tips_hash = chain_status.tips_hash.clone().unwrap(); + // tips_hash.sort(); // if it is a dag block - if HashValue::sha3_256_of(&tips_hash.encode().expect("hash encode must be successful")) - != new_block_parent - { - // or a block of a single chain - verify_block!( - VerifyBlockField::Header, - expect_number == new_block_header.number(), - "Invalid block: Unexpect block number, expect:{}, got: {}.", - expect_number, - new_block_header.number() - ); - - verify_block!( - VerifyBlockField::Header, - current_id == new_block_parent, - "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", - current_id, - new_block_parent, - new_block_header.number() - ); - } - } else { + // if HashValue::sha3_256_of(&tips_hash.encode().expect("hash encode must be successful")) + // != new_block_parent + // { + // // or a block of a single chain + // verify_block!( + // VerifyBlockField::Header, + // expect_number == new_block_header.number(), + // "Invalid block: Unexpect block number, expect:{}, got: {}.", + // expect_number, + // new_block_header.number() + // ); + + // verify_block!( + // VerifyBlockField::Header, + // current_id == new_block_parent, + // "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", + // current_id, + // new_block_parent, + // new_block_header.number() + // ); + // } + // } else { // or a block of a single chain verify_block!( VerifyBlockField::Header, @@ -226,7 +227,7 @@ impl BlockVerifier for BasicVerifier { new_block_parent, new_block_header.number() ); - } + // } verify_block!( VerifyBlockField::Header, new_block_header.timestamp() > current.timestamp(), diff --git a/chain/tests/block_test_utils.rs b/chain/tests/block_test_utils.rs index d6f5c833d0..34ae965304 100644 --- a/chain/tests/block_test_utils.rs +++ b/chain/tests/block_test_utils.rs @@ -79,6 +79,7 @@ fn gen_header( parent_header.chain_id(), 0, BlockHeaderExtra::new([0u8; 4]), + None, ) } @@ -260,7 +261,7 @@ proptest! { // blocks in ; for block in blocks { if !block.header().is_genesis() { - let result = block_chain.apply(block.clone(), None, &mut None); + let result = block_chain.apply(block.clone()); info!("{:?}", result); } } diff --git a/chain/tests/test_block_chain.rs b/chain/tests/test_block_chain.rs index 999e0578c3..f300d279e3 100644 --- a/chain/tests/test_block_chain.rs +++ b/chain/tests/test_block_chain.rs @@ -131,11 +131,11 @@ fn test_block_chain() -> Result<()> { let mut mock_chain = MockChain::new(ChainNetwork::new_test())?; let block = mock_chain.produce()?; assert_eq!(block.header().number(), 1); - mock_chain.apply(block, None)?; + mock_chain.apply(block)?; assert_eq!(mock_chain.head().current_header().number(), 1); let block = mock_chain.produce()?; assert_eq!(block.header().number(), 2); - mock_chain.apply(block, None)?; + mock_chain.apply(block)?; assert_eq!(mock_chain.head().current_header().number(), 2); Ok(()) } @@ -200,7 +200,7 @@ fn gen_uncle() -> (MockChain, BlockChain, BlockHeader) { let miner = mock_chain.miner(); let block = product_a_block(&fork_block_chain, miner, Vec::new()); let uncle_block_header = block.header().clone(); - fork_block_chain.apply(block, None, &mut None).unwrap(); + fork_block_chain.apply(block, None).unwrap(); (mock_chain, fork_block_chain, uncle_block_header) } @@ -221,7 +221,7 @@ fn test_uncle() { // 3. mock chain apply let uncles = vec![uncle_block_header.clone()]; let block = product_a_block(mock_chain.head(), miner, uncles); - mock_chain.apply(block, None).unwrap(); + mock_chain.apply(block).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_some()); assert!(mock_chain .head() @@ -240,7 +240,7 @@ fn test_uncle_exist() { // 3. mock chain apply let uncles = vec![uncle_block_header.clone()]; let block = product_a_block(mock_chain.head(), &miner, uncles); - mock_chain.apply(block, None).unwrap(); + mock_chain.apply(block).unwrap(); assert!(mock_chain.head().head_block().block.uncles().is_some()); assert!(mock_chain .head() @@ -254,7 +254,7 @@ fn test_uncle_exist() { // 4. uncle exist let uncles = vec![uncle_block_header]; let block = product_a_block(mock_chain.head(), &miner, uncles); - assert!(mock_chain.apply(block, None).is_err()); + assert!(mock_chain.apply(block).is_err()); } #[stest::test(timeout = 120)] @@ -264,12 +264,12 @@ fn test_uncle_son() { // 3. uncle son let uncle_son = product_a_block(&fork_block_chain, miner, Vec::new()); let uncle_son_block_header = uncle_son.header().clone(); - fork_block_chain.apply(uncle_son, None).unwrap(); + fork_block_chain.apply(uncle_son).unwrap(); // 4. mock chain apply let uncles = vec![uncle_son_block_header]; let block = product_a_block(mock_chain.head(), miner, uncles); - assert!(mock_chain.apply(block, None).is_err()); + assert!(mock_chain.apply(block).is_err()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } @@ -281,7 +281,7 @@ fn test_random_uncle() { // 3. random BlockHeader and apply let uncles = vec![BlockHeader::random()]; let block = product_a_block(mock_chain.head(), miner, uncles); - assert!(mock_chain.apply(block, None).is_err()); + assert!(mock_chain.apply(block).is_err()); assert_eq!(mock_chain.head().current_epoch_uncles_size(), 0); } @@ -350,7 +350,7 @@ fn test_uncle_in_diff_epoch() { // 5. mock chain apply let uncles = vec![uncle_block_header]; let block = product_a_block(mock_chain.head(), &miner, uncles); - assert!(mock_chain.apply(block, None).is_err()); + assert!(mock_chain.apply(block).is_err()); } #[stest::test(timeout = 480)] diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 1514cd0c26..3c6203ffb3 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -27,6 +27,7 @@ parking_lot = { workspace = true } itertools = { workspace = true } starcoin-config = { workspace = true } bcs-ext = { workspace = true } +starcoin-accumulator = { package = "starcoin-accumulator", workspace = true } [dev-dependencies] proptest = { workspace = true } diff --git a/consensus/src/consensus_test.rs b/consensus/src/consensus_test.rs index b878fa9e10..e0d52d7606 100644 --- a/consensus/src/consensus_test.rs +++ b/consensus/src/consensus_test.rs @@ -89,6 +89,7 @@ fn verify_header_test_barnard_block3_ubuntu22() { ChainId::new(251), 2894404328, BlockHeaderExtra::new([0u8; 4]), + None, ); G_CRYPTONIGHT .verify_header_difficulty(header.difficulty(), &header) diff --git a/consensus/src/dag/blockdag.rs b/consensus/src/dag/blockdag.rs index e23c8d1d00..728f76c5cd 100644 --- a/consensus/src/dag/blockdag.rs +++ b/consensus/src/dag/blockdag.rs @@ -1,6 +1,7 @@ use super::ghostdag::protocol::{ColoringOutput, GhostdagManager}; use super::reachability::{inquirer, reachability_service::MTReachabilityService}; use super::types::ghostdata::GhostdagData; +use crate::FlexiDagStorageConfig; use crate::consensusdb::prelude::StoreError; use crate::consensusdb::schemadb::GhostdagStoreReader; use crate::consensusdb::{ @@ -11,15 +12,26 @@ use crate::consensusdb::{ }, }; use anyhow::{anyhow, bail, Ok}; +use bcs_ext::BCSCodec; use parking_lot::RwLock; +use starcoin_accumulator::accumulator_info::AccumulatorInfo; +use starcoin_accumulator::{MerkleAccumulator, Accumulator}; +use starcoin_accumulator::node::AccumulatorStoreType; +use starcoin_config::NodeConfig; use starcoin_crypto::HashValue as Hash; +use starcoin_storage::flexi_dag::SyncFlexiDagSnapshot; +use starcoin_storage::storage::CodecKVStore; +use starcoin_storage::{Store, SyncFlexiDagStore, Storage, BlockStore}; +use starcoin_types::block::BlockNumber; +use starcoin_types::startup_info; use starcoin_types::{ blockhash::{BlockHashes, KType, ORIGIN}, header::{ConsensusHeader, DagHeader}, }; use std::collections::HashMap; use std::collections::HashSet; -use std::sync::Arc; +use std::path::Path; +use std::sync::{Arc, Mutex}; pub type DbGhostdagManager = GhostdagManager< DbGhostdagStore, @@ -28,6 +40,14 @@ pub type DbGhostdagManager = GhostdagManager< DbHeadersStore, >; +#[derive(Clone)] +pub enum InitDagState { + FailedToInitDag, + InitDagSuccess(Arc>), + InitedDag, + NoNeedInitDag, +} + #[derive(Clone)] pub struct BlockDAG { genesis_hash: Hash, @@ -70,6 +90,55 @@ impl BlockDAG { dag } + pub fn try_init_with_storage(storage: Arc, config: Arc) -> anyhow::Result<(Option, Option)> { + let startup_info = storage.get_startup_info()?.expect("startup info must exist"); + if let Some(key) = startup_info.get_dag_main() { + let accumulator_info = storage.get_dag_accumulator_info()?.expect("dag accumulator info should exist"); + assert!(accumulator_info.get_num_leaves() > 0, "the number of dag accumulator leaf must be greater than 0"); + let dag_accumulator = MerkleAccumulator::new_with_info( + accumulator_info, + storage.get_accumulator_store(AccumulatorStoreType::SyncDag), + ); + let dag_genesis_hash = dag_accumulator.get_leaf(0)?.expect("the genesis in dag accumulator must none be none"); + + let dag_genesis_header = storage.get_block_header_by_hash(dag_genesis_hash)?.expect("the genesis block in dag accumulator must none be none"); + + Ok((Some(Self::new_by_config(DagHeader::new_genesis(dag_genesis_header), config.data_dir().join("flexidag").as_path())?), Some(dag_accumulator))) + + } else { + let block_header = storage.get_block_header_by_hash(startup_info.get_main().clone())?.expect("the genesis block in dag accumulator must none be none"); + let fork_height = storage.dag_fork_height(config.net().id().clone()); + if block_header.number() < fork_height { + Ok((None, None)) + } else if block_header.number() == fork_height { + let dag_accumulator = MerkleAccumulator::new_with_info(AccumulatorInfo::default(), storage.get_accumulator_store(AccumulatorStoreType::SyncDag)); + dag_accumulator.append(&[block_header.id()])?; + storage.get_accumulator_snapshot_storage().put(Self::calculate_dag_accumulator_key(vec![block_header.id()])?, SyncFlexiDagSnapshot { + child_hashes: vec![block_header.id()], + accumulator_info: dag_accumulator.get_info(), + head_block_id: block_header.id(), + })?; + Ok((Some(Self::new_by_config(DagHeader::new_genesis(block_header), config.data_dir().join("flexidag").as_path())?), Some(dag_accumulator))) + } else { + bail!("failed to init dag") + } + } + } + + pub fn new_by_config(header: DagHeader, db_path: &Path) -> anyhow::Result { + let config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); + let db = FlexiDagStorage::create_from_path(db_path, config)?; + let dag = Self::new(header.hash(), 16, db); + Ok(dag) + } + + pub fn calculate_dag_accumulator_key(mut tips: Vec) -> anyhow::Result { + tips.sort(); + Ok(Hash::sha3_256_of(&tips.encode().expect( + "encoding the sorted relatship set must be successful", + ))) + } + pub fn clear_missing_block(&mut self) { self.missing_blocks.clear(); } @@ -81,11 +150,11 @@ impl BlockDAG { self.relations_store .insert(Hash::new(ORIGIN), BlockHashes::new(vec![])) .unwrap(); - let _ = self.addToDag(genesis); + let _ = self.add_to_dag(genesis); Ok(()) } - pub fn addToDag(&mut self, header: DagHeader) -> anyhow::Result { + pub fn add_to_dag(&mut self, header: DagHeader) -> anyhow::Result { //TODO:check genesis // Generate ghostdag data let parents_hash = header.parents_hash(); @@ -136,7 +205,7 @@ impl BlockDAG { if is_orphan_block { return Ok(()); } - self.addToDag(header.clone()); + self.add_to_dag(header.clone()); self.check_missing_block(header)?; Ok(()) } @@ -146,7 +215,7 @@ impl BlockDAG { for orphan in orphans.iter() { let is_orphan = self.is_orphan(&orphan)?; if !is_orphan { - self.addToDag(header.clone()); + self.add_to_dag(header.clone()); } } } @@ -254,7 +323,7 @@ mod tests { .expect("Failed to create flexidag storage"); let mut dag = BlockDAG::new(genesis_hash, k, db); dag.init_with_genesis(genesis); - let block = DagHeader::new(BlockHeader::random(), vec![genesis_hash]); - dag.addToDag(block); + let block = DagHeader::new(BlockHeader::random()); + dag.add_to_dag(block); } } diff --git a/flexidag/Cargo.toml b/flexidag/Cargo.toml new file mode 100644 index 0000000000..79d1439fa4 --- /dev/null +++ b/flexidag/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "starcoin-flexidag" +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +publish = { workspace = true } +version = "1.13.7" +homepage = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +starcoin-config = { workspace = true } +starcoin-crypto = { workspace = true } +starcoin-logger = { workspace = true } +starcoin-service-registry = { workspace = true } +starcoin-storage = { workspace = true } +starcoin-types = { workspace = true } +tokio = { workspace = true } +starcoin-consensus = { workspace = true } +starcoin-accumulator = { workspace = true } +thiserror = { workspace = true } + diff --git a/flexidag/src/flexidag_service.rs b/flexidag/src/flexidag_service.rs new file mode 100644 index 0000000000..84e78050f3 --- /dev/null +++ b/flexidag/src/flexidag_service.rs @@ -0,0 +1,334 @@ +use std::sync::Arc; + +use anyhow::{anyhow, Result, Ok, bail, Error}; +use starcoin_accumulator::{Accumulator, MerkleAccumulator, accumulator_info::AccumulatorInfo}; +use starcoin_config::NodeConfig; +use starcoin_consensus::{BlockDAG, dag::types::ghostdata::GhostdagData}; +use starcoin_crypto::HashValue; +use starcoin_service_registry::{ActorService, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest}; +use starcoin_storage::{storage::CodecKVStore, flexi_dag::SyncFlexiDagSnapshot, Storage, SyncFlexiDagStore, BlockStore}; +use starcoin_types::{block::BlockHeader, startup_info, header::DagHeader}; + +#[derive(Debug, Clone)] +pub struct DumpTipsToAccumulator { + pub block_header: BlockHeader, + pub current_head_block_id: HashValue, +} + +impl ServiceRequest for DumpTipsToAccumulator { + type Response = anyhow::Result<()>; +} + +#[derive(Debug, Clone)] +pub struct UpdateDagTips { + pub block_header: BlockHeader, + pub current_head_block_id: HashValue, +} + +impl ServiceRequest for UpdateDagTips { + type Response = anyhow::Result<()>; +} + +#[derive(Debug, Clone)] +pub struct GetDagTips; + +impl ServiceRequest for GetDagTips { + type Response = anyhow::Result>>; +} + +#[derive(Debug, Clone)] +pub struct GetDagAccumulatorInfo; + +impl ServiceRequest for GetDagAccumulatorInfo { + type Response = anyhow::Result>; +} + +#[derive(Debug, Clone)] +pub struct GetDagAccumulatorLeafDetail { + pub leaf_index: u64, + pub batch_size: u64, +} + +#[derive(Debug, Clone)] +pub struct DagAccumulatorLeafDetail { + pub accumulator_root: HashValue, + pub tips: Vec, +} + +impl ServiceRequest for GetDagAccumulatorLeafDetail { + type Response = anyhow::Result>; +} + +#[derive(Debug, Clone)] +pub struct GetDagBlockParents { + pub block_id: HashValue, +} + +#[derive(Debug, Clone)] +pub struct DagBlockParents { + pub parents: Vec, +} + +impl ServiceRequest for GetDagBlockParents { + type Response = anyhow::Result; +} + +#[derive(Debug, Clone)] +pub struct GetDagAccumulatorLeaves { + pub leaf_index: u64, + pub batch_size: u64, + pub reverse: bool, +} + +#[derive(Debug, Clone)] +pub struct DagAccumulatorLeaf { + pub leaf_index: u64, + pub dag_accumulator_root: HashValue, +} + +impl ServiceRequest for GetDagAccumulatorLeaves { + type Response = anyhow::Result>; +} + +#[derive(Debug, Clone)] +pub struct AddToDag { + pub block_header: BlockHeader, +} + +#[derive(Debug, Clone)] +pub struct MergesetBlues { + pub selected_parent: HashValue, + pub mergeset_blues: Vec, +} + +impl ServiceRequest for AddToDag { + type Response = anyhow::Result; +} + + +pub struct FlexidagService { + dag: Option, + dag_accumulator: Option, + tips: Option>, // some is for dag or the state of the chain is still in old version + storage: Arc, +} + +impl FlexidagService { + pub fn add_to_dag( + &mut self, + header: BlockHeader, + ) -> Result> { + let dag = match &mut self.dag { + Some(dag) => dag, + None => bail!("dag is none"), + }; + match dag.get_ghostdag_data(header.id()) + { + std::result::Result::Ok(ghost_dag_data) => Ok(ghost_dag_data), + Err(_) => std::result::Result::Ok(Arc::new( + // jacktest: TODO:add_to_dag should not use parents hash since the block header has them + dag.add_to_dag(DagHeader::new(header.clone()))?, + )), + } + } +} + +impl ServiceFactory for FlexidagService { + fn create(ctx: &mut ServiceContext) -> Result { + let storage = ctx.get_shared::>()?; + let config = ctx.get_shared::>()?; + let (dag, dag_accumulator) = BlockDAG::try_init_with_storage(storage.clone(), config.clone())?; + let tips = dag_accumulator.as_ref().map(|accumulator| { + let tips_index = accumulator.num_leaves(); + let tips_key = accumulator + .get_leaf(tips_index) + .expect("failed to read the dag snapshot hash") + .expect("the dag snapshot hash is none"); + storage + .get_accumulator_snapshot_storage() + .get(tips_key) + .expect("failed to read the snapsho object") + .expect("dag snapshot object is none") + .child_hashes + }); + Ok(Self { + dag, + dag_accumulator, + tips, + storage: storage.clone(), + }) + } +} + +impl ActorService for FlexidagService { + fn started(&mut self, ctx: &mut ServiceContext) -> Result<()> { + // ctx.subscribe::(); + Ok(()) + } + + fn stopped(&mut self, ctx: &mut ServiceContext) -> Result<()> { + // ctx.unsubscribe::(); + Ok(()) + } +} + +// send this message after minting a new block +// and the block was committed +// and startup info was updated +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: DumpTipsToAccumulator, ctx: &mut ServiceContext) -> Result<()> { + let storage = ctx.get_shared::>()?; + if self.tips.is_none() { + let config = ctx.get_shared::>()?; + let (dag, dag_accumulator) = BlockDAG::try_init_with_storage(storage, config)?; + if dag.is_none() { + Ok(()) // the chain is still in single chain + } else { + // initialize the dag data, the chain will be the dag chain at next block + self.dag = dag; + self.tips = Some(vec![msg.block_header.id()]); + self.dag_accumulator = dag_accumulator; + + Ok(()) + } + } else { + // the chain had became the flexidag chain + let tips = self.tips.take().expect("the tips should not be none in this branch"); + let key = BlockDAG::calculate_dag_accumulator_key(tips.clone())?; + let dag = self.dag_accumulator.as_mut().expect("the tips is not none but the dag accumulator is none"); + dag.append(&vec![key])?; + storage.get_accumulator_snapshot_storage().put(key, SyncFlexiDagSnapshot { + child_hashes: tips, + accumulator_info: dag.get_info(), + head_block_id: msg.current_head_block_id, + })?; + dag.flush()?; + self.tips = Some(vec![msg.block_header.id()]); + Ok(()) + } + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: UpdateDagTips, ctx: &mut ServiceContext) -> Result<()> { + let header = msg.block_header; + match self.tips.clone() { + Some(mut tips) => { + if !tips.contains(&header.id()) { + tips.push(header.id()); + self.tips = Some(tips); + } + Ok(()) + } + None => { + let storage = ctx.get_shared::>()?; + let config = ctx.get_shared::>()?; + if header.number() == storage.dag_fork_height(config.net().id().clone()) { + let (dag, dag_accumulator) = BlockDAG::try_init_with_storage(storage.clone(), config)?; + if dag.is_none() { + Ok(()) // the chain is still in single chain + } else { + // initialize the dag data, the chain will be the dag chain at next block + self.dag = dag; + self.tips = Some(vec![header.id()]); + self.dag_accumulator = dag_accumulator; + + storage.get_startup_info()?.map(|mut startup_info| { + startup_info.dag_main = Some(header.id()); + storage.save_startup_info(startup_info) + }).expect("starup info should not be none") + } + } else { + Ok(()) // drop the block, the chain is still in single chain + } + } + } + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, _msg: GetDagTips, _ctx: &mut ServiceContext) -> Result>> { + Ok(self.tips.clone()) + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, _msg: GetDagAccumulatorInfo, _ctx: &mut ServiceContext) -> Result> { + Ok(self.dag_accumulator.as_ref().map(|dag_accumulator_info| { + dag_accumulator_info.get_info() + })) + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: GetDagAccumulatorLeaves, _ctx: &mut ServiceContext) -> Result> { + match &self.dag_accumulator { + Some(dag_accumulator) => { + let end_index = std::cmp::min( + msg.leaf_index + msg.batch_size - 1, + dag_accumulator.num_leaves() - 1, + ); + let mut result = vec![]; + for index in msg.leaf_index..=end_index { + let real_index = if msg.reverse { + end_index - index + 1 + } else { + index + }; + let key = dag_accumulator.get_leaf(real_index)?.ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; + let snaptshot = self.storage.get_accumulator_snapshot_storage().get(key)?.expect("the snapshot should not be none"); + result.push(DagAccumulatorLeaf { + leaf_index: real_index, + dag_accumulator_root: snaptshot.accumulator_info.accumulator_root, + }); + } + Ok(result) + } + None => bail!("dag accumulator is none"), + } + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: GetDagBlockParents, _ctx: &mut ServiceContext) -> Result { + match &self.dag { + Some(dag) => Ok(DagBlockParents { parents: dag.get_parents(msg.block_id)? } ) , + None => bail!("dag is none"), + } + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: GetDagAccumulatorLeafDetail, _ctx: &mut ServiceContext) -> Result> { + match &self.dag_accumulator { + Some(dag_accumulator) => { + let end_index = std::cmp::min( + msg.leaf_index + msg.batch_size - 1, + dag_accumulator.num_leaves() - 1, + ); + let mut details = vec![]; + let snapshot_storage = self.storage.get_accumulator_snapshot_storage(); + for index in msg.leaf_index..=end_index { + let key = dag_accumulator.get_leaf(index)?.ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; + let snapshot = snapshot_storage.get(key)?.ok_or_else(|| anyhow!("the dag snapshot is none"))?; + details.push(DagAccumulatorLeafDetail { + accumulator_root: snapshot.accumulator_info.accumulator_root, + tips: snapshot.child_hashes, + }); + } + Ok(details) + } + None => bail!("dag accumulator is none"), + } + } +} + +impl ServiceHandler for FlexidagService { + fn handle(&mut self, msg: AddToDag, _ctx: &mut ServiceContext) -> Result { + let ghost_dag_data = self.add_to_dag(msg.block_header)?; + Ok(MergesetBlues { + selected_parent: ghost_dag_data.selected_parent, + mergeset_blues: ghost_dag_data.mergeset_blues.as_ref().clone(), + }) + } +} diff --git a/flexidag/src/lib.rs b/flexidag/src/lib.rs new file mode 100644 index 0000000000..66689bb05f --- /dev/null +++ b/flexidag/src/lib.rs @@ -0,0 +1,17 @@ +pub mod flexidag_service; +pub use flexidag_service::FlexidagService; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/miner/Cargo.toml b/miner/Cargo.toml index d5180be4e1..6a023bf48c 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -27,6 +27,8 @@ starcoin-txpool-api = { workspace = true } starcoin-vm-types = { workspace = true } tokio = { features = ["full"], workspace = true } starcoin-types = { package = "starcoin-types", workspace = true } +starcoin-flexidag = { workspace = true } +async-std = { workspace = true } [dev-dependencies] starcoin-network-rpc = { package = "starcoin-network-rpc", workspace = true } diff --git a/miner/src/create_block_template/mod.rs b/miner/src/create_block_template/mod.rs index f18c1a7a4e..4a91130f14 100644 --- a/miner/src/create_block_template/mod.rs +++ b/miner/src/create_block_template/mod.rs @@ -13,10 +13,12 @@ use starcoin_config::NodeConfig; use starcoin_consensus::Consensus; use starcoin_crypto::hash::HashValue; use starcoin_executor::VMMetrics; +use starcoin_flexidag::flexidag_service::GetDagTips; +use starcoin_flexidag::{FlexidagService, flexidag_service}; use starcoin_logger::prelude::*; use starcoin_open_block::OpenedBlock; use starcoin_service_registry::{ - ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest, + ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest, ServiceRef, }; use starcoin_storage::{BlockStore, Storage, Store}; use starcoin_txpool::TxPoolService; @@ -79,6 +81,7 @@ impl ServiceFactory for BlockBuilderService { .and_then(|registry| BlockBuilderMetrics::register(registry).ok()); let vm_metrics = ctx.get_shared_opt::()?; + let flexidag_service = ctx.service_ref::()?.clone(); let inner = Inner::new( config.net(), storage, @@ -87,6 +90,7 @@ impl ServiceFactory for BlockBuilderService { config.miner.block_gas_limit, miner_account, metrics, + flexidag_service, vm_metrics, )?; Ok(Self { inner }) @@ -190,6 +194,7 @@ pub struct Inner

{ local_block_gas_limit: Option, miner_account: AccountInfo, metrics: Option, + flexidag_service: ServiceRef, vm_metrics: Option, } @@ -205,6 +210,7 @@ where local_block_gas_limit: Option, miner_account: AccountInfo, metrics: Option, + flexidag_service: ServiceRef, vm_metrics: Option, ) -> Result { let chain = BlockChain::new( @@ -224,6 +230,7 @@ where local_block_gas_limit, miner_account, metrics, + flexidag_service, vm_metrics, }) } @@ -315,10 +322,7 @@ where let author = *self.miner_account.address(); let previous_header = self.chain.current_header(); - let tips_header = match self.chain.status().tips_hash { - Some(_) => self.chain.status().tips_hash, - None => None, - }; + let dag_block_parents = self.get_dag_block_parents(); let uncles = self.find_uncles(); let mut now_millis = self.chain.time_service().now_millis(); @@ -366,4 +370,8 @@ where template, }) } + + fn get_dag_block_parents(&self) -> Result>> { + Ok(async_std::task::block_on(self.flexidag_service.send(GetDagTips))??) + } } diff --git a/network-rpc/api/src/lib.rs b/network-rpc/api/src/lib.rs index 7732942889..5ea816fcc7 100644 --- a/network-rpc/api/src/lib.rs +++ b/network-rpc/api/src/lib.rs @@ -281,7 +281,7 @@ pub trait NetworkRpc: Sized + Send + Sync + 'static { &self, peer_id: PeerId, ids: Vec, - ) -> BoxFuture>, Option)>>>>; + ) -> BoxFuture>>>; fn get_state_with_table_item_proof( &self, diff --git a/network/api/src/peer_provider.rs b/network/api/src/peer_provider.rs index 0987895bbf..fda14bba1e 100644 --- a/network/api/src/peer_provider.rs +++ b/network/api/src/peer_provider.rs @@ -314,6 +314,14 @@ impl PeerSelector { } } + pub fn peer_infos(&self) -> Vec { + self.details + .lock() + .iter() + .map(|peer| peer.peer_info) + .collect() + } + pub fn peers(&self) -> Vec { self.details .lock() diff --git a/network/src/service.rs b/network/src/service.rs index 045538e8a0..cdad26aadb 100644 --- a/network/src/service.rs +++ b/network/src/service.rs @@ -484,7 +484,7 @@ impl Inner { } pub(crate) fn update_chain_status(&mut self, sync_status: SyncStatus) { - let chain_status = sync_status.chain_status().clone(); + let chain_status: ChainStatus = sync_status.chain_status().clone(); self.self_peer .peer_info .update_chain_status(chain_status.clone()); @@ -559,7 +559,6 @@ impl Inner { peer_info.peer_info.update_chain_status(ChainStatus::new( block_header.clone(), compact_block_message.block_info.clone(), - compact_block_message.tips_hash.clone(), )); if self.self_peer.known_blocks.contains(&block_id) { @@ -721,7 +720,6 @@ impl Inner { ChainStatus::new( msg.compact_block.header.clone(), msg.block_info.clone(), - msg.tips_hash.clone(), ) .encode() .expect("Encoding the compact_block.header and block_info must be successful"), diff --git a/network/types/src/peer_info.rs b/network/types/src/peer_info.rs index 13b0463afb..ca3c898301 100644 --- a/network/types/src/peer_info.rs +++ b/network/types/src/peer_info.rs @@ -71,6 +71,8 @@ impl PeerInfo { pub fn update_dag_accumulator_info(&mut self, dag_accumulator_info: Option) { self.chain_info .update_dag_accumulator_info(dag_accumulator_info) + + } /// This peer is support notification diff --git a/node/src/node.rs b/node/src/node.rs index bb75b58903..a15d0534f7 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -227,8 +227,6 @@ impl ServiceHandler for NodeService { let result = connect_service .send(ExecuteRequest { block, - dag_block_parent: parents, - dag_transaction_parent: None, }) .await??; info!("Re execute result: {:?}", result); @@ -369,14 +367,10 @@ impl NodeService { let (chain_info, genesis) = Genesis::init_and_check_storage(config.net(), storage.clone(), config.data_dir())?; - let flex_dag_config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); - let flex_dag_db = FlexiDagStorage::create_from_path("./smolstc", flex_dag_config) - .expect("Failed to create flexidag storage"); - - let mut dag = BlockDAG::new(genesis.block().id(), 8, flex_dag_db); - dag.init_with_genesis(DagHeader::new_genesis(genesis.block().header().clone())) - .expect("dag init with genesis"); - registry.put_shared(Arc::new(Mutex::new(dag))).await?; + match BlockDAG::init_with_storage(storage.clone(), config.clone())? { + Some(dag) => registry.put_shared(Arc::new(dag)).await?, + None => info!("dag will be initialized later when the height of the chain reaches the specific one"), + } info!( "Start node with chain info: {}, number {} upgrade_time cost {} secs, ", diff --git a/storage/src/flexi_dag/mod.rs b/storage/src/flexi_dag/mod.rs index c3333272d7..6c86f98551 100644 --- a/storage/src/flexi_dag/mod.rs +++ b/storage/src/flexi_dag/mod.rs @@ -16,6 +16,7 @@ use starcoin_crypto::HashValue; pub struct SyncFlexiDagSnapshot { pub child_hashes: Vec, // child nodes(tips), to get the relationship, use dag's relationship store pub accumulator_info: AccumulatorInfo, + pub head_block_id: HashValue, // to initialize the BlockInfo } impl ValueCodec for SyncFlexiDagSnapshot { diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 4712cf5da9..598b72d764 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -21,17 +21,18 @@ use network_p2p_types::peer_id::PeerId; use num_enum::{IntoPrimitive, TryFromPrimitive}; use once_cell::sync::Lazy; use starcoin_accumulator::{ - accumulator_info::AccumulatorInfo, node::AccumulatorStoreType, AccumulatorTreeStore, + accumulator_info::{AccumulatorInfo, self}, node::AccumulatorStoreType, AccumulatorTreeStore, }; use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; use starcoin_logger::prelude::info; use starcoin_state_store_api::{StateNode, StateNodeStore}; use starcoin_types::{ - block::{Block, BlockBody, BlockHeader, BlockInfo}, + block::{Block, BlockBody, BlockHeader, BlockInfo, BlockNumber}, blockhash::ORIGIN, contract_event::ContractEvent, - startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo}, + header, + startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo, self}, transaction::{RichTransactionInfo, Transaction}, }; use starcoin_vm_types::{ @@ -330,13 +331,9 @@ pub trait SyncFlexiDagStore { new_tips: Vec, accumulator_info: AccumulatorInfo, ) -> Result<()>; - fn get_dag_accumulator_info(&self, block_id: HashValue) -> Result>; + fn get_dag_accumulator_info(&self) -> Result>; fn get_tips_by_block_id(&self, block_id: HashValue) -> Result>; - fn get_flexidag_init_data( - &self, - head_block: &BlockHeader, - id: ChainNetworkID, - ) -> Result<(Option, Option>)>; + fn dag_fork_height(&self, id: ChainNetworkID) -> BlockNumber; } // TODO: remove Arc, we can clone Storage directly. @@ -455,14 +452,11 @@ impl BlockStore for Storage { format_err!("Startup block info {:?} should exist", startup_info.main) })?; - let (flexi_dag_accumulator_info, tips_header_hash) = - self.get_flexidag_init_data(&head_block, id)?; - let chain_info = ChainInfo::new( head_block.chain_id(), genesis_hash, - ChainStatus::new(head_block.clone(), head_block_info, tips_header_hash), - flexi_dag_accumulator_info, + ChainStatus::new(head_block.clone(), head_block_info), + self.get_dag_accumulator_info()?, ); Ok(Some(chain_info)) } @@ -675,17 +669,23 @@ impl SyncFlexiDagStore for Storage { self.flexi_dag_storage.get_snapshot_storage() } - fn get_dag_accumulator_info(&self, block_id: HashValue) -> Result> { - match self - .flexi_dag_storage - .get_snapshot_storage() - .get(block_id)? - { - Some(snapshot) => Ok(Some(snapshot.accumulator_info)), - None => Ok(None), + fn get_dag_accumulator_info(&self) -> Result> { + let startup_info = self.get_startup_info()?; + if startup_info.is_none() { + return Ok(None); + } + + let dag_main = startup_info.unwrap().get_dag_main(); + if dag_main.is_none() { + return Ok(None); } + + let dag_main = dag_main.unwrap(); + + Ok(Some(self.flexi_dag_storage.get_snapshot_storage().get(dag_main)?.expect("snapshot should not be none").accumulator_info)) } + // update dag accumulator fn append_dag_accumulator_leaf( &self, key: HashValue, @@ -697,46 +697,28 @@ impl SyncFlexiDagStore for Storage { accumulator_info: accumulator_info.clone(), }; // for sync - if self.flexi_dag_storage.get_hashes_by_hash(key)?.is_some() { - if let Some(t) = self.flexi_dag_storage.get_hashes_by_hash(key)? { - if t != snapshot { - bail!("the accumulator differ from other"); - } + if let Some(t) = self.flexi_dag_storage.get_hashes_by_hash(key)? { + if t != snapshot { + panic!("the accumulator differ from other"); } + } else { + self.flexi_dag_storage.put_hashes(key, snapshot)?; } - self.flexi_dag_storage.put_hashes(key, snapshot.clone())?; - - // for block chain - new_tips.iter().try_fold((), |_, block_id| { - if let Some(t) = self - .flexi_dag_storage - .get_hashes_by_hash(block_id.clone())? - { - if t != snapshot { - bail!("the key {} should not exists", block_id); - } - } - self.flexi_dag_storage - .put_hashes(block_id.clone(), snapshot.clone()) - })?; + Ok(()) } - fn get_tips_by_block_id(&self, block_id: HashValue) -> Result> { - match self.query_by_hash(block_id)? { + fn get_tips_by_block_id(&self, key: HashValue) -> Result> { + match self.query_by_hash(key)? { Some(snapshot) => Ok(snapshot.child_hashes), None => { - bail!("failed to get snapshot by hash: {}", block_id); + bail!("failed to get snapshot by hash: {}", key); } } } - fn get_flexidag_init_data( - &self, - head_block: &BlockHeader, - id: ChainNetworkID, - ) -> Result<(Option, Option>)> { - let flexi_dag_number = match id { + fn dag_fork_height(&self, id: ChainNetworkID) -> BlockNumber { + match id { ChainNetworkID::Builtin(network_id) => match network_id { starcoin_config::BuiltinNetworkID::Test => TEST_FLEXIDAG_FORK_HEIGHT, starcoin_config::BuiltinNetworkID::Dev => DEV_FLEXIDAG_FORK_HEIGHT, @@ -746,28 +728,7 @@ impl SyncFlexiDagStore for Storage { starcoin_config::BuiltinNetworkID::Main => MAIN_FLEXIDAG_FORK_HEIGHT, }, ChainNetworkID::Custom(_) => DEV_FLEXIDAG_FORK_HEIGHT, - }; - - let (dag_accumulator_info, tips) = if flexi_dag_number == head_block.number() { - ( - Some(AccumulatorInfo::default()), - Some(vec![head_block.id()]), - ) - } else if flexi_dag_number < head_block.number() { - let dag_accumulator_info = self - .get_dag_accumulator_info(head_block.id())? - .expect("the dag accumulator info must exist!"); - let tips = self.get_tips_by_block_id(head_block.id())?; - assert!( - tips.len() > 0, - "the length of the tips must be greater than 0" - ); - (Some(dag_accumulator_info), Some(tips)) - } else { - (None, None) - }; - - Ok((dag_accumulator_info, tips)) + } } } diff --git a/storage/src/tests/test_block.rs b/storage/src/tests/test_block.rs index 4e663c57b7..0024af03de 100644 --- a/storage/src/tests/test_block.rs +++ b/storage/src/tests/test_block.rs @@ -43,6 +43,7 @@ fn test_block() { ChainId::test(), 0, BlockHeaderExtra::new([0u8; 4]), + None, ); storage .block_storage @@ -102,6 +103,7 @@ fn test_block_number() { ChainId::test(), 0, BlockHeaderExtra::new([0u8; 4]), + None, ); storage .block_storage @@ -149,6 +151,7 @@ fn test_old_failed_block_decode() { ChainId::test(), 0, BlockHeaderExtra::new([0u8; 4]), + None, ); let block_body = BlockBody::new(vec![SignedUserTransaction::mock()], None); @@ -185,6 +188,7 @@ fn test_save_failed_block() { ChainId::test(), 0, BlockHeaderExtra::new([0u8; 4]), + None, ); let block_body = BlockBody::new(vec![SignedUserTransaction::mock()], None); diff --git a/storage/src/tests/test_storage.rs b/storage/src/tests/test_storage.rs index 90e2cdecff..1c098eeba8 100644 --- a/storage/src/tests/test_storage.rs +++ b/storage/src/tests/test_storage.rs @@ -25,6 +25,9 @@ use starcoin_types::transaction::{ RichTransactionInfo, SignedUserTransaction, Transaction, TransactionInfo, }; use starcoin_types::vm_error::KeptVMStatus; +use starcoin_vm_types::account_address::AccountAddress; +use starcoin_vm_types::language_storage::TypeTag; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; //use starcoin_vm_types::account_address::AccountAddress; //use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use std::path::Path; @@ -296,7 +299,7 @@ fn generate_old_db(path: &Path) -> Result> { BlockBody::new(vec![txn.clone()], None), ); let mut txn_inf_ids = vec![]; - let block_metadata = block.to_metadata(0, None); + let block_metadata = block.to_metadata(0); let txn_info_0 = TransactionInfo::new( block_metadata.id(), HashValue::random(), diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 256457897d..7210d528ca 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -44,6 +44,7 @@ sysinfo = { workspace = true } thiserror = { workspace = true } starcoin-consensus = { workspace = true } timeout-join-handler = { workspace = true } +starcoin-flexidag = { workspace = true } [dev-dependencies] hex = { workspace = true } diff --git a/sync/api/src/lib.rs b/sync/api/src/lib.rs index d541abfa25..5bb31fea85 100644 --- a/sync/api/src/lib.rs +++ b/sync/api/src/lib.rs @@ -27,15 +27,13 @@ pub struct StartSyncTxnEvent; pub struct PeerNewBlock { peer_id: PeerId, new_block: Block, - dag_parents: Option>, } impl PeerNewBlock { - pub fn new(peer_id: PeerId, new_block: Block, dag_parents: Option>) -> Self { + pub fn new(peer_id: PeerId, new_block: Block) -> Self { PeerNewBlock { peer_id, new_block, - dag_parents, } } @@ -46,10 +44,6 @@ impl PeerNewBlock { pub fn get_block(&self) -> &Block { &self.new_block } - - pub fn get_dag_parents(&self) -> &Option> { - &self.dag_parents - } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index ae776b9cfa..52ea7ed771 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -13,14 +13,16 @@ use network_api::PeerProvider; use starcoin_chain_api::{ChainReader, ConnectBlockError, WriteableChainService}; use starcoin_config::{NodeConfig, G_CRATE_VERSION}; use starcoin_consensus::BlockDAG; +use starcoin_consensus::dag::blockdag::InitDagState; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; +use starcoin_flexidag::FlexidagService; use starcoin_logger::prelude::*; use starcoin_network::NetworkServiceRef; use starcoin_service_registry::{ ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, }; -use starcoin_storage::{BlockStore, Storage}; +use starcoin_storage::{BlockStore, Storage, flexi_dag}; use starcoin_sync_api::PeerNewBlock; use starcoin_txpool::TxPoolService; use starcoin_txpool_api::TxPoolSyncService; @@ -29,6 +31,7 @@ use starcoin_txpool_mock_service::MockTxPoolService; use starcoin_types::block::ExecutedBlock; use starcoin_types::sync_status::SyncStatus; use starcoin_types::system_events::{MinedBlock, SyncStatusChangeEvent, SystemShutdown}; +use std::result; use std::sync::{Arc, Mutex}; use sysinfo::{DiskExt, System, SystemExt}; @@ -132,7 +135,6 @@ where .get_startup_info()? .ok_or_else(|| format_err!("Startup info should exist."))?; let vm_metrics = ctx.get_shared_opt::()?; - let dag = ctx.get_shared::>>()?; let chain_service = WriteBlockChainService::new( config.clone(), startup_info, @@ -140,7 +142,7 @@ where txpool, bus, vm_metrics, - dag.clone(), + ctx.service_ref::()?.clone(), )?; Ok(Self::new(chain_service, config)) @@ -209,7 +211,7 @@ impl EventHandler for BlockConnectorService { - if let Err(e) = self.chain_service.try_connect(block, msg.dag_parents) { + if let Err(e) = self.chain_service.try_connect(block) { error!("Process connected new block from sync error: {:?}", e); } } @@ -239,7 +241,7 @@ impl EventHandler for BlockConnectorService { - if let Err(e) = self.chain_service.apply_failed(block, msg.dag_parents) { + if let Err(e) = self.chain_service.apply_failed(block) { error!("Process connected new block from sync error: {:?}", e); } } @@ -259,16 +261,19 @@ impl EventHandler where TransactionPoolServiceT: TxPoolSyncService + 'static, { - fn handle_event(&mut self, msg: MinedBlock, _ctx: &mut ServiceContext) { + fn handle_event(&mut self, msg: MinedBlock, ctx: &mut ServiceContext) { let MinedBlock(new_block, tips_headers) = msg; + let block_header = new_block.header().clone(); let id = new_block.header().id(); debug!("try connect mined block: {}", id); - match self - .chain_service - .try_connect(new_block.as_ref().clone(), tips_headers) - { - std::result::Result::Ok(_) => debug!("Process mined block {} success.", id), + match self.chain_service.try_connect(block) { + std::result::Result::Ok(_) => { + match self.chain_service.dump_tips(block_header) { + std::result::Result::Ok(_) => (), + Err(e) => error!("failed to dump tips to dag accumulator: {}", e), + } + } Err(e) => { warn!("Process mined block {} fail, error: {:?}", id, e); } @@ -299,12 +304,13 @@ where let peer_id = msg.get_peer_id(); if let Err(e) = self .chain_service - .try_connect(msg.get_block().clone(), msg.get_dag_parents().clone()) + .try_connect(msg.get_block().clone()) { match e.downcast::() { std::result::Result::Ok(connect_error) => { match connect_error { ConnectBlockError::FutureBlock(block) => { + self.chain_service.update_tips(msg.get_block().header().clone())?; //TODO cache future block if let std::result::Result::Ok(sync_service) = ctx.service_ref::() @@ -379,7 +385,7 @@ where msg: ExecuteRequest, _ctx: &mut ServiceContext>, ) -> Result { - self.chain_service.execute(msg.block, msg.dag_block_parent) + self.chain_service.execute(msg.block, msg.block_parent) } } diff --git a/sync/src/block_connector/mod.rs b/sync/src/block_connector/mod.rs index 3e69c86527..a567f76c35 100644 --- a/sync/src/block_connector/mod.rs +++ b/sync/src/block_connector/mod.rs @@ -38,8 +38,6 @@ impl ServiceRequest for ResetRequest { #[derive(Debug, Clone)] pub struct ExecuteRequest { pub block: Block, - pub dag_block_parent: Option>, - pub dag_transaction_parent: Option, } impl ServiceRequest for ExecuteRequest { diff --git a/sync/src/block_connector/test_illegal_block.rs b/sync/src/block_connector/test_illegal_block.rs index 5bf7e28542..84f642c0ef 100644 --- a/sync/src/block_connector/test_illegal_block.rs +++ b/sync/src/block_connector/test_illegal_block.rs @@ -155,7 +155,7 @@ fn apply_legal_block( ) .unwrap(); writeable_block_chain_service - .try_connect(new_block, None) + .try_connect(new_block) .unwrap(); } @@ -294,7 +294,7 @@ async fn test_verify_consensus(succ: bool) -> Result<()> { .with_difficulty(U256::from(1024u64)) .build(); } - main.apply(new_block, None)?; + main.apply(new_block)?; Ok(()) } @@ -782,7 +782,7 @@ async fn test_verify_uncles_uncle_exist_failed() { .create_block(block_template, net.time_service().as_ref()) .unwrap(); writeable_block_chain_service - .try_connect(new_block, None) + .try_connect(new_block) .unwrap(); info!( @@ -852,7 +852,7 @@ async fn test_verify_uncle_and_parent_number_failed() { .create_block(block_template, net.time_service().as_ref()) .unwrap(); writeable_block_chain_service - .try_connect(new_block, None) + .try_connect(new_block) .unwrap(); let new_number = writeable_block_chain_service .get_main() diff --git a/sync/src/block_connector/test_write_block_chain.rs b/sync/src/block_connector/test_write_block_chain.rs index 2c4822d492..39401352c3 100644 --- a/sync/src/block_connector/test_write_block_chain.rs +++ b/sync/src/block_connector/test_write_block_chain.rs @@ -65,7 +65,7 @@ pub async fn create_writeable_block_chain() -> ( txpool_service, bus, None, - Arc::new(Mutex::new(dag)), + Some(Arc::new(Mutex::new(dag))), ) .unwrap(), node_config, @@ -86,7 +86,7 @@ pub fn gen_blocks( writeable_block_chain_service, time_service, ); - let e = writeable_block_chain_service.try_connect(block, None); + let e = writeable_block_chain_service.try_connect(block); println!("try_connect result: {:?}", e) } } @@ -165,7 +165,7 @@ fn gen_fork_block_chain( parent_id = block.id(); writeable_block_chain_service - .try_connect(block, None) + .try_connect(block) .unwrap(); } } diff --git a/sync/src/block_connector/test_write_dag_block_chain.rs b/sync/src/block_connector/test_write_dag_block_chain.rs index 175bb0a5ce..21932ea913 100644 --- a/sync/src/block_connector/test_write_dag_block_chain.rs +++ b/sync/src/block_connector/test_write_dag_block_chain.rs @@ -38,7 +38,6 @@ pub fn gen_dag_blocks( last_block_hash = Some(block.id()); let e = writeable_block_chain_service.try_connect( block, - writeable_block_chain_service.get_main().status().tips_hash, ); println!("try_connect result: {:?}", e); assert!(e.is_ok()); @@ -137,7 +136,7 @@ fn gen_fork_dag_block_chain( parent_id = block.id(); writeable_block_chain_service - .try_connect(block, None) + .try_connect(block) .unwrap(); } return Some(parent_id); diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index fa28c36de1..697432b491 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -2,15 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 use crate::block_connector::metrics::ChainMetrics; +use crate::tasks::BlockDiskCheckEvent; use anyhow::{bail, format_err, Ok, Result}; use async_std::stream::StreamExt; +use futures::executor::block_on; use starcoin_chain::BlockChain; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, WriteableChainService}; use starcoin_config::NodeConfig; +use starcoin_consensus::dag::blockdag::InitDagState; +use starcoin_consensus::dag::ghostdag; use starcoin_consensus::dag::ghostdag::protocol::ColoringOutput; -use starcoin_consensus::BlockDAG; +use starcoin_consensus::dag::types::ghostdata::GhostdagData; +use starcoin_consensus::{BlockDAG, FlexiDagStorage, FlexiDagStorageConfig}; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; +use starcoin_flexidag::FlexidagService; +use starcoin_flexidag::flexidag_service::{AddToDag, DumpTipsToAccumulator, UpdateDagTips}; use starcoin_logger::prelude::*; use starcoin_service_registry::bus::{Bus, BusService}; use starcoin_service_registry::{ServiceContext, ServiceRef}; @@ -46,7 +53,7 @@ where metrics: Option, vm_metrics: Option, dag_block_pool: Arc)>>>, - dag: Arc>, + flexidag_service: ServiceRef, } #[derive(Clone, Debug)] @@ -103,16 +110,16 @@ impl

WriteableChainService for WriteBlockChainService

where P: TxPoolSyncService + 'static, { - fn try_connect(&mut self, block: Block, parents_hash: Option>) -> Result<()> { + fn try_connect(&mut self, block: Block) -> Result<()> { let _timer = self .metrics .as_ref() .map(|metrics| metrics.chain_block_connect_time.start_timer()); - let result = if parents_hash.is_some() { + let result = if block.header().parents_hash().is_some() { + assert!(transaction_parent.is_some(), "in dag branch, the transaction parent should not be none"); self.connect_dag_inner( block, - parents_hash.expect("parents_hash should not be None"), ) } else { self.connect_inner(block) @@ -149,7 +156,7 @@ where txpool: TransactionPoolServiceT, bus: ServiceRef, vm_metrics: Option, - dag: Arc>, + flexidag_service: ServiceRef, ) -> Result { let net = config.net(); let main = BlockChain::new( @@ -174,7 +181,7 @@ where metrics, vm_metrics, dag_block_pool: Arc::new(Mutex::new(vec![])), - dag, + flexidag_service, }) } @@ -241,14 +248,13 @@ where pub fn apply_failed( &mut self, block: Block, - parents_hash: Option>, ) -> Result<()> { use anyhow::bail; use starcoin_chain::verifier::FullVerifier; // apply but no connection let verified_block = self.main.verify_with_verifier::(block)?; - let _executed_block = self.main.execute(verified_block, parents_hash)?; + let _executed_block = self.main.execute(verified_block)?; bail!("failed to apply for tesing the connection later!"); } @@ -278,11 +284,21 @@ where if branch_total_difficulty > main_total_difficulty { self.update_startup_info(new_branch.head_block().header())?; - let dag_parents = self.dag.lock().unwrap().get_parents(new_head_block)?; - ctx.broadcast(NewHeadBlock( - Arc::new(new_branch.head_block()), - Some(dag_parents), - )); + if self.dag.is_none() { + ctx.broadcast(NewHeadBlock(Arc::new(new_branch.head_block()), None)); + } else { + let dag_parents = self + .dag + .as_ref() + .expect("the dag should not be None") + .lock() + .expect("failed to lock the dag") + .get_parents(new_head_block)?; + ctx.broadcast(NewHeadBlock( + Arc::new(new_branch.head_block()), + Some(dag_parents), + )); + } Ok(()) } else { @@ -293,7 +309,6 @@ where pub fn select_head( &mut self, new_branch: BlockChain, - dag_block_parents: Option>, ) -> Result<()> { let executed_block = new_branch.head_block(); let main_total_difficulty = self.main.get_total_difficulty()?; @@ -301,18 +316,11 @@ where let parent_is_main_head = self.is_main_head(&executed_block.header().parent_hash()); if branch_total_difficulty > main_total_difficulty { - let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = - if dag_block_parents.is_some() { - // for dag - self.find_ancestors_from_dag_accumulator(&new_branch)? + let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = if !parent_is_main_head { + self.find_ancestors_from_accumulator(&new_branch)? } else { - // for single chain - if !parent_is_main_head { - self.find_ancestors_from_accumulator(&new_branch)? - } else { - (1, vec![executed_block.block.clone()], 0, vec![]) - } - }; + (1, vec![executed_block.block.clone()], 0, vec![]) + }; self.main = new_branch; self.do_new_head( @@ -324,7 +332,7 @@ where )?; } else { //send new branch event - self.broadcast_new_branch(executed_block, dag_block_parents); + self.broadcast_new_branch(executed_block); } Ok(()) } @@ -439,7 +447,7 @@ where pub fn execute( &mut self, block: Block, - dag_block_parent: Option>, + transaction_parent: Option, ) -> Result { let chain = BlockChain::new( self.config.net().time_service(), @@ -449,7 +457,7 @@ where self.vm_metrics.clone(), )?; let verify_block = chain.verify(block)?; - chain.execute(verify_block, dag_block_parent) + chain.execute(verify_block) } fn is_main_head(&self, parent_id: &HashValue) -> bool { @@ -662,7 +670,6 @@ where fn broadcast_new_branch( &self, block: ExecutedBlock, - dag_block_parents: Option>, ) { if let Some(metrics) = self.metrics.as_ref() { metrics @@ -672,7 +679,7 @@ where } if let Err(e) = self .bus - .broadcast(NewBranch(Arc::new(block), dag_block_parents)) + .broadcast(NewBranch(Arc::new(block))) { error!("Broadcast NewBranch error: {:?}", e); } @@ -681,9 +688,6 @@ where fn switch_branch( &mut self, block: Block, - dag_block_parents: Option>, - dag_block_next_parent: Option, - next_tips: &mut Option>, ) -> Result { let (block_info, fork) = self.find_or_fork( block.header(), @@ -700,7 +704,7 @@ where branch.get_total_difficulty()? ); let exe_block = branch.head_block(); - self.select_head(branch, dag_block_parents)?; + self.select_head(branch)?; if let Some(new_tips) = next_tips { new_tips.push(block_info.block_id().clone()); } @@ -713,7 +717,6 @@ where let executed_block = self.main.connect(ExecutedBlock { block: block.clone(), block_info, - parents_hash: dag_block_parents, })?; info!( "Block {} main has been processed, trigger head selection", @@ -724,19 +727,16 @@ where } (None, Some(mut branch)) => { // the block is not in the block, but the parent is - let result = branch.apply(block, dag_block_parents.clone()); + let result = branch.apply(block, None); let executed_block = result?; - self.select_head(branch, dag_block_parents)?; + self.select_head(branch)?; Ok(ConnectOk::ExeConnectBranch(executed_block)) } (None, None) => Err(ConnectBlockError::FutureBlock(Box::new(block)).into()), } } - fn connect_to_main( - &mut self, - block: Block, - ) -> Result { + fn connect_to_main(&mut self, block: Block) -> Result { let block_id = block.id(); if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH && block.header().number() == starcoin_storage::BARNARD_HARD_FORK_HEIGHT @@ -752,52 +752,98 @@ where if self.main.current_header().id() == block.header().parent_hash() && !self.block_exist(block_id)? { - return self.apply_and_select_head(block, None, None, &mut None); + return self.apply_and_select_head(block); } // todo: should switch dag together - self.switch_branch(block, None, None, &mut None) + self.switch_branch(block) } fn apply_and_select_head( &mut self, block: Block, - dag_block_parents: Option>, - dag_block_next_parent: Option, - next_tips: &mut Option>, ) -> Result { - let executed_block = self.main.apply(block, dag_block_parents)?; + let executed_block = self.main.apply(block, None)?; let enacted_blocks = vec![executed_block.block().clone()]; self.do_new_head(executed_block.clone(), 1, enacted_blocks, 0, vec![])?; return Ok(ConnectOk::ExeConnectMain(executed_block)); } + fn add_to_dag( + &mut self, + header: &BlockHeader, + ) -> Result> { + let dag = self.dag.as_mut().expect("dag must be inited before using"); + match dag + .lock() + .expect("dag must be inited before using") + .get_ghostdag_data(header.id()) + { + std::result::Result::Ok(ghost_dag_data) => Ok(ghost_dag_data), + Err(_) => std::result::Result::Ok(Arc::new( + dag.lock() + .expect("failed to lock the dag") + .add_to_dag(DagHeader::new(header.clone()))?, + )), + } + } + fn connect_dag_inner( &mut self, block: Block, - parents_hash: Vec, ) -> Result { - let ghost_dag_data = self - .dag - .lock() - .unwrap() - .addToDag(DagHeader::new(block.header, parents_hash.clone()))?; + let add_dag_result = async_std::task::block_on(self.flexidag_service.send(AddToDag { + block_header: block.header().clone(), + }))??; let selected_parent = self .storage - .get_block_by_hash(ghost_dag_data.selected_parent)? + .get_block_by_hash(add_dag_result.selected_parent)? .expect("selected parent should in storage"); let mut chain = self.main.fork(selected_parent.header.parent_hash())?; - for blue_hash in ghost_dag_data.mergeset_blues.iter() { + let mut transaction_parent = chain.status().head().id().clone(); + for blue_hash in add_dag_result.mergeset_blues.mergeset_blues.iter() { if let Some(blue_block) = self.storage.get_block(blue_hash.to_owned())? { - chain.apply(blue_block, Some(parents_hash.clone())); + match chain.apply(blue_block, Some(transaction_parent)) { + Ok(executed_block) => transaction_parent = executed_block, + Err(_) => warn!("failed to connect dag block: {:?}", e), + } } else { error!("Failed to get block {:?}", blue_hash); return Ok(ConnectOk::DagConnectMissingBlock); } } - //self.broadcast_new_head(); + // select new head and update startup info(main but dag main) + self.select_head_for_dag(chain)?; Ok(ConnectOk::DagConnected) } + fn select_head_for_dag(&self, new_chain: BlockChain) -> Result<()> { + + + Ok(()) + } + + pub fn dump_tips(&self, block_header: BlockHeader) -> Result<()> { + if block_header.number() < self.storage.dag_fork_height(self.config.net().id().clone()) { + Ok(()) + } else { + self.flexidag_service.send( DumpTipsToAccumulator { + block_header, + current_head_block_id: self.main.status().head().id().clone(), + }) + } + } + + pub fn update_tips(&self, block_header: BlockHeader) -> Result<()> { + if block_header.number() >= self.storage.dag_fork_height(self.config.net().id().clone()) { + self.flexidag_service.send(UpdateDagTips { + block_header, + current_head_block_id: self.main.status().head().id().clone(), + }) + } else { + Ok(()) // nothing to do + } + } + fn connect_inner(&mut self, block: Block) -> Result { let block_id = block.id(); if block_id == *starcoin_storage::BARNARD_HARD_FORK_HASH @@ -812,9 +858,7 @@ where } // normal block, just connect to main // let mut next_tips = Some(vec![]); - let executed_block = self - .connect_to_main(block)? - .clone(); + let executed_block = self.connect_to_main(block)?.clone(); if let Some(block) = executed_block.block() { self.broadcast_new_head(block.clone(), None, None); } diff --git a/sync/src/sync.rs b/sync/src/sync.rs index 856ab5ffc5..6d8622b1f0 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -5,7 +5,7 @@ use crate::block_connector::BlockConnectorService; use crate::sync_metrics::SyncMetrics; use crate::tasks::{full_sync_task, sync_dag_full_task, AncestorEvent, SyncFetcher}; use crate::verified_rpc_client::{RpcVerifyError, VerifiedRpcClient}; -use anyhow::{format_err, Result}; +use anyhow::{format_err, Result, Ok}; use futures::executor::block_on; use futures::FutureExt; use futures_timer::Delay; @@ -17,11 +17,13 @@ use starcoin_chain_api::{ChainReader, ChainWriter}; use starcoin_config::NodeConfig; use starcoin_consensus::BlockDAG; use starcoin_executor::VMMetrics; +use starcoin_flexidag::{FlexidagService, flexidag_service}; +use starcoin_flexidag::flexidag_service::GetDagAccumulatorInfo; use starcoin_logger::prelude::*; use starcoin_network::NetworkServiceRef; use starcoin_network::PeerEvent; use starcoin_service_registry::{ - ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, + ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRef, }; use starcoin_storage::block_info::BlockInfoStore; use starcoin_storage::{BlockStore, Storage, Store, SyncFlexiDagStore}; @@ -65,6 +67,7 @@ pub struct SyncService { storage: Arc, metrics: Option, peer_score_metrics: Option, + flexidag_service: ServiceRef, vm_metrics: Option, } @@ -72,6 +75,7 @@ impl SyncService { pub fn new( config: Arc, storage: Arc, + flexidag_service: ServiceRef, vm_metrics: Option, ) -> Result { let startup_info = storage @@ -97,7 +101,7 @@ impl SyncService { // .get_genesis()? // .ok_or_else(|| format_err!("Can not find genesis hash in storage."))?; let dag_accumulator_info = - match storage.get_dag_accumulator_info(head_block_info.block_id().clone())? { + match storage.get_dag_accumulator_info()? { Some(info) => Some(info), None => { warn!( @@ -112,7 +116,6 @@ impl SyncService { ChainStatus::new( head_block.header.clone(), head_block_info, - Some(storage.get_tips_by_block_id(head_block_hash)?), ), dag_accumulator_info, ), @@ -121,6 +124,7 @@ impl SyncService { storage, metrics, peer_score_metrics, + flexidag_service, vm_metrics, }) } @@ -262,14 +266,10 @@ impl SyncService { network.clone(), )); - // for testing, we start dag sync directly - if let Some((target, op_dag_accumulator_info)) = - rpc_client.get_best_target(current_block_info.get_total_difficulty())? - { - if let Some(target_accumulator_info) = op_dag_accumulator_info { - let local_dag_accumulator_info = storage - .get_dag_accumulator_info(current_block_id)? - .expect("current dag accumulator info should exist"); + let op_local_dag_accumulator_info = self.flexidag_service.send(GetDagAccumulatorInfo).await??; + + if let Some(local_dag_accumulator_info) = op_local_dag_accumulator_info { + let dag_sync_futs = rpc_client.get_dag_targets()?.into_iter().fold(Ok(vec![]), |mut futs, target_accumulator_infos| { let (fut, task_handle, task_event_handle) = sync_dag_full_task( local_dag_accumulator_info, target_accumulator_info, @@ -295,8 +295,23 @@ impl SyncService { if let Some(sync_task_total) = sync_task_total.as_ref() { sync_task_total.with_label_values(&["start"]).inc(); } - Ok(Some(fut.await?)) + futs.and_then(|v| { + v.push(fut) + }) + })?.into_iter().fold(Ok(vec![]), |chain, fut| { + Ok(vec![fut.await?]) + })?; + assert!(dag_sync_futs.len() <= 1); + if dag_sync_futs.len() == 1 { + Ok(Some(dag_sync_futs[0])) } else { + debug!("[sync]No best peer to request, current is beast."); + Ok(None) + } + } else { + if let Some((target, _)) = + rpc_client.get_best_target(current_block_info.get_total_difficulty())? + { info!("[sync] Find target({}), total_difficulty:{}, current head({})'s total_difficulty({})", target.target_id.id(), target.block_info.total_difficulty, current_block_id, current_block_info.total_difficulty); let (fut, task_handle, task_event_handle) = full_sync_task( @@ -326,10 +341,10 @@ impl SyncService { sync_task_total.with_label_values(&["start"]).inc(); } Ok(Some(fut.await?)) + } else { + debug!("[sync]No best peer to request, current is beast."); + Ok(None) } - } else { - debug!("[sync]No best peer to request, current is beast."); - Ok(None) } }; let network = ctx.get_shared::()?; @@ -357,7 +372,7 @@ impl SyncService { let current_block_id = startup_info.main; let local_dag_accumulator_info = test_storage - .get_dag_accumulator_info(current_block_id).unwrap() + .get_dag_accumulator_info().unwrap() .expect("current dag accumulator info should exist"); if let Some(sync_task_total) = sync_task_total.as_ref() { @@ -456,8 +471,9 @@ impl ServiceFactory for SyncService { fn create(ctx: &mut ServiceContext) -> Result { let config = ctx.get_shared::>()?; let storage = ctx.get_shared::>()?; + let flexidag_service = ctx.service_ref::()?.clone(); let vm_metrics = ctx.get_shared_opt::()?; - Self::new(config, storage, vm_metrics) + Self::new(config, storage, flexidag_service, vm_metrics) } } @@ -671,11 +687,10 @@ impl EventHandler for SyncService { if self.sync_status.update_chain_status(ChainStatus::new( block.header().clone(), block.block_info.clone(), - tips_hash, )) { self.sync_status.update_dag_accumulator_info( self.storage - .get_dag_accumulator_info(block.header().id()) + .get_dag_accumulator_info() .expect("dag accumulator info must exist"), ); ctx.broadcast(SyncStatusChangeEvent(self.sync_status.clone())); diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 19d0a399b4..a1f95580a8 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -11,10 +11,12 @@ use network_api::PeerProvider; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::{verifier::BasicVerifier, BlockChain}; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, ExecutedBlock}; -use starcoin_config::G_CRATE_VERSION; +use starcoin_config::{G_CRATE_VERSION, Connect}; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; +use starcoin_flexidag::FlexidagService; use starcoin_logger::prelude::*; +use starcoin_service_registry::ServiceRef; use starcoin_storage::BARNARD_HARD_FORK_HASH; use starcoin_sync_api::SyncTarget; use starcoin_types::block::{Block, BlockIdAndNumber, BlockInfo, BlockNumber}; @@ -32,8 +34,6 @@ pub struct SyncBlockData { pub(crate) peer_id: Option, pub(crate) accumulator_root: Option, // the block belongs to this accumulator leaf pub(crate) count_in_leaf: u64, // the number of the block in the accumulator leaf - pub(crate) dag_block_headers: Option>, - pub(crate) dag_transaction_header: Option, } impl SyncBlockData { @@ -52,8 +52,6 @@ impl SyncBlockData { peer_id, accumulator_root, count_in_leaf, - dag_block_headers, - dag_transaction_header, } } } @@ -64,8 +62,6 @@ impl Block, Option, Option, - Option>, - Option, )> for SyncBlockData { fn into( @@ -74,15 +70,11 @@ impl Block, Option, Option, - Option>, - Option, ) { ( self.block, self.info, self.peer_id, - self.dag_block_headers, - self.dag_transaction_header, ) } } @@ -238,7 +230,7 @@ pub struct BlockCollector { last_accumulator_root: HashValue, dag_block_pool: Vec, target_accumulator_root: HashValue, - dag: Option>>, + flexidag_service: ServiceRef, } impl BlockCollector @@ -254,7 +246,7 @@ where peer_provider: N, skip_pow_verify: bool, target_accumulator_root: HashValue, - dag: Option>>, + flexidag_service: ServiceRef, ) -> Self { if let Some(dag) = &dag { dag.lock() @@ -271,7 +263,7 @@ where last_accumulator_root: HashValue::zero(), dag_block_pool: Vec::new(), target_accumulator_root, - dag: dag.clone(), + flexidag_service, } } @@ -282,7 +274,7 @@ where parents_hash: Option>, next_tips: &mut Option>, ) -> Result<()> { - self.apply_block(block, None, parents_hash, next_tips) + self.apply_block(block, None) } fn notify_connected_block( @@ -354,8 +346,6 @@ where &mut self, block: Block, peer_id: Option, - parents_hash: Option>, - next_tips: &mut Option>, ) -> Result<()> { if let Some((_failed_block, pre_peer_id, err, version)) = self .chain @@ -385,9 +375,9 @@ where } let apply_result = if self.skip_pow_verify { self.chain - .apply_with_verifier::(block.clone(), parents_hash) + .apply_with_verifier::(block.clone()) } else { - self.chain.apply(block.clone(), parents_hash) + self.chain.apply(block.clone()) }; if let Err(err) = apply_result { let error_msg = err.to_string(); @@ -494,33 +484,48 @@ where self.notify_connected_block(block, block_info, action, state?, None) } - fn collect_item( + fn collect_dag_item( &mut self, item: SyncBlockData, - next_tips: &mut Option>, - ) -> Result<(Block, BlockInfo, Option>, BlockConnectAction)> { - let (block, block_info, peer_id, parents_hash, dag_transaction_header) = item.into(); + ) -> Result<()> { + let (block, block_info, peer_id) = item.into(); let block_id = block.id(); let timestamp = block.header().timestamp(); - if let Some(parents) = parents_hash.clone() { - if let Some(dag) = &self.dag { - // let color = dag - // .lock() - // .unwrap() - // .commit_header(&Header::new(block.header().clone(), parents.clone()))?; - // if let ColoringOutput::Red = color { - // panic!("the red block should not be applied or connected!"); - // } - let _ = dag - .lock() - .unwrap() - .push_parent_children(block_id, Arc::new(parents)); + let add_dag_result = async_std::task::block_on(self.flexidag_service.send(AddToDag { + block_header: block.header().clone(), + }))??; + let selected_parent = self + .storage + .get_block_by_hash(add_dag_result.selected_parent)? + .expect("selected parent should in storage"); + let mut chain = self.chain.fork(selected_parent.header.parent_hash())?; + for blue_hash in add_dag_result.mergeset_blues.mergeset_blues.iter() { + if let Some(blue_block) = self.storage.get_block(blue_hash.to_owned())? { + match chain.apply(blue_block) { + Ok(_executed_block) => (), + Err(e) => warn!("failed to connect dag block: {:?}", e), + } } else { - panic!("in dag sync, the dag should not be None") + error!("Failed to get block {:?}", blue_hash); } } + if chain.status().info().total_difficulty > self.chain.status().info().total_difficulty { + self.chain = chain; + } + + Ok(()) + } + + fn collect_item( + &mut self, + item: SyncBlockData, + ) -> Result<(Block, BlockInfo, BlockConnectAction)> { + let (block, block_info, peer_id) = item.into(); + let block_id = block.id(); + let timestamp = block.header().timestamp(); + return match block_info { Some(block_info) => { //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. @@ -528,24 +533,21 @@ where self.chain.connect(ExecutedBlock { block: block.clone(), block_info: block_info.clone(), - parents_hash: parents_hash.clone(), })?; let block_info = self.chain.status().info; Ok(( block, block_info, - parents_hash, BlockConnectAction::ConnectExecutedBlock, )) } None => { - self.apply_block(block.clone(), peer_id, parents_hash.clone(), next_tips)?; + self.apply_block(block.clone(), peer_id)?; self.chain.time_service().adjust(timestamp); let block_info = self.chain.status().info; Ok(( block, block_info, - parents_hash, BlockConnectAction::ConnectNewBlock, )) } @@ -641,12 +643,17 @@ where assert!(!process_block_pool.is_empty()); - let mut next_tips = Some(vec![]); + // let mut next_tips = Some(vec![]); let mut block_to_broadcast = vec![]; - for item in process_block_pool { - block_to_broadcast.push(self.collect_item(item, &mut next_tips)?) + if item.accumulator_root.is_some() { + for item in process_block_pool { + self.collect_dag_item(item)? + } + } else { + for item in process_block_pool { + block_to_broadcast.push(self.collect_item(item)?) + } } - //verify target match self.target { Some(_) => { @@ -660,14 +667,6 @@ where self.broadcast_single_chain_block(block, block_info, action) } None => { - // dag - assert!(!next_tips - .as_ref() - .expect("next_tips should not be None") - .is_empty()); - self.chain.append_dag_accumulator_leaf( - next_tips.expect("next_tips should not be None"), - )?; self.broadcast_dag_chain_block(block_to_broadcast) } } diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 17c6c81421..cf92b552fb 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -39,6 +39,12 @@ use stream_task::{ }; pub trait SyncFetcher: PeerOperator + BlockIdFetcher + BlockFetcher + BlockInfoFetcher { + fn get_dag_targets(&self) -> Result> { + Ok(self.peer_selector().peer_infos().into_iter().map(|peer_info| { + peer_info.chain_info().dag_accumulator_info().clone() + }).collect()); + } + fn get_best_target( &self, min_difficulty: U256, @@ -303,8 +309,6 @@ pub trait BlockFetcher: Send + Sync { Vec<( Block, Option, - Option>, - Option, )>, >, >; @@ -323,8 +327,6 @@ where Vec<( Block, Option, - Option>, - Option, )>, >, > { @@ -342,21 +344,12 @@ impl BlockFetcher for VerifiedRpcClient { Vec<( Block, Option, - Option>, - Option, )>, >, > { self.get_blocks(block_ids.clone()) .and_then(|blocks| async move { - let results: Result< - Vec<( - Block, - Option, - Option>, - Option, - )>, - > = block_ids + let results = block_ids .iter() .zip(blocks) .map(|(id, block)| { diff --git a/sync/src/tasks/sync_dag_block_task.rs b/sync/src/tasks/sync_dag_block_task.rs index 7187f25e23..4e31565e0f 100644 --- a/sync/src/tasks/sync_dag_block_task.rs +++ b/sync/src/tasks/sync_dag_block_task.rs @@ -58,13 +58,6 @@ impl SyncDagBlockTask { .expect(format!("index: {} must be valid for getting snapshot", index).as_str()) .expect(format!("index: {} should not be None for getting snapshot", index).as_str()); - // let block_with_infos = self - // .local_store - // .get_block_with_info(snapshot.child_hashes.clone())?; - - // assert_eq!(block_with_infos.len(), snapshot.child_hashes.len()); - - // the order must be the same between snapshot.child_hashes and block_with_infos let mut absent_block = vec![]; let mut result = vec![]; snapshot.child_hashes.iter().for_each(|block_id| { @@ -83,14 +76,12 @@ impl SyncDagBlockTask { .fetch_blocks(absent_block) .await? .iter() - .map(|(block, peer_info, parents, transaction_header)| { + .map(|(block, peer_info)| { ( block.header().id(), ( block.clone(), peer_info.clone(), - parents.clone(), - transaction_header.clone(), ), ) }) @@ -110,22 +101,7 @@ impl SyncDagBlockTask { .expect("the block should be got from peer already") .1 .to_owned(); - block_info.dag_parents = fetched_block_info - .get(&block_info.block_id) - .expect("the block should be got from peer already") - .2 - .to_owned() - .expect("dag block should have parents"); - block_info.dag_transaction_header = Some( - fetched_block_info - .get(&block_info.block_id) - .expect("the block should be got from peer already") - .3 - .to_owned() - .expect("dag block should have parents"), - ); }); - result.sort_by_key(|item| item.block_id); let block_info = self .local_store @@ -140,11 +116,6 @@ impl SyncDagBlockTask { peer_id: item.peer_id, accumulator_root: Some(snapshot.accumulator_info.get_accumulator_root().clone()), count_in_leaf: snapshot.child_hashes.len() as u64, - dag_block_headers: Some(item.dag_parents), - dag_transaction_header: Some( - item.dag_transaction_header - .expect("dag transaction header should exists"), - ), }) .collect()) } diff --git a/sync/src/tasks/sync_dag_full_task.rs b/sync/src/tasks/sync_dag_full_task.rs index 9eee187011..f13e78275c 100644 --- a/sync/src/tasks/sync_dag_full_task.rs +++ b/sync/src/tasks/sync_dag_full_task.rs @@ -170,6 +170,12 @@ fn get_start_block_id( .clone()) } +async fn sync_dag_block2( + start_index: u64, + accumulator: MerkleAccumulator, +) -> anyhow::Result { +} + async fn sync_dag_block( start_index: u64, accumulator: MerkleAccumulator, @@ -194,16 +200,16 @@ where let event_handle = Arc::new(TaskEventCounterHandle::new()); let ext_error_handle = Arc::new(ExtSyncTaskErrorHandle::new(fetcher.clone())); - let start_block_id = get_start_block_id(&accumulator, start_index, local_store.clone()) - .map_err(|err| TaskError::BreakError(anyhow!(err))); - let chain = BlockChain::new( - time_service.clone(), - start_block_id?, - local_store.clone(), - net_id, - vm_metrics, - ) - .map_err(|err| TaskError::BreakError(anyhow!(err))); + // let start_block_id = get_start_block_id(&accumulator, start_index, local_store.clone()) + // .map_err(|err| TaskError::BreakError(anyhow!(err))); + // let chain = BlockChain::new( + // time_service.clone(), + // start_block_id?, + // local_store.clone(), + // net_id, + // vm_metrics, + // ) + // .map_err(|err| TaskError::BreakError(anyhow!(err))); let leaf = accumulator .get_leaf(start_index) @@ -221,16 +227,16 @@ where .as_str(), ); - snapshot.child_hashes.sort(); - let last_chain_block = snapshot - .child_hashes - .iter() - .last() - .expect("block id should not be None") - .clone(); + let chain = BlockChain::new( + time_service.clone(), + snapshot.head_block_id?, + local_store.clone(), + net_id, + vm_metrics, + )?; let current_block_info = local_store - .get_block_info(last_chain_block)? + .get_block_info(snapshot.head_block_id)? .ok_or_else(|| format_err!("Can not find block info by id: {}", last_chain_block)) .map_err(|err| TaskError::BreakError(anyhow!(err))); diff --git a/sync/src/verified_rpc_client.rs b/sync/src/verified_rpc_client.rs index 57c682d320..7272f12e7b 100644 --- a/sync/src/verified_rpc_client.rs +++ b/sync/src/verified_rpc_client.rs @@ -386,8 +386,6 @@ impl VerifiedRpcClient { Option<( Block, Option, - Option>, - Option, )>, >, > { @@ -412,7 +410,7 @@ impl VerifiedRpcClient { ); None } else { - Some((block.0, Some(peer_id.clone()), block.1, block.2)) + Some((block.0, Some(peer_id.clone()))) } } else { None diff --git a/types/src/block.rs b/types/src/block.rs index 7b6c31ac09..53301e93c9 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -845,6 +845,10 @@ impl BlockInfo { pub fn block_id(&self) -> &HashValue { &self.block_id } + + pub fn transaction_parent(&self) -> Option { + self.transaction_parent.clone() + } } impl Sample for BlockInfo { @@ -1066,15 +1070,13 @@ impl BlockTemplate { pub struct ExecutedBlock { pub block: Block, pub block_info: BlockInfo, - pub parents_hash: Option>, } impl ExecutedBlock { - pub fn new(block: Block, block_info: BlockInfo, dag_parents: Option>) -> Self { + pub fn new(block: Block, block_info: BlockInfo) -> Self { ExecutedBlock { block, block_info, - parents_hash: dag_parents, } } diff --git a/types/src/header.rs b/types/src/header.rs index 438bef47f0..644f1bb064 100644 --- a/types/src/header.rs +++ b/types/src/header.rs @@ -1,4 +1,4 @@ -use crate::block::BlockHeader; +use crate::block::{BlockHeader, BlockNumber}; use crate::blockhash::{BlockLevel, ORIGIN}; use crate::U256; use serde::{Deserialize, Serialize}; @@ -19,10 +19,10 @@ pub struct DagHeader { } impl DagHeader { - pub fn new(block_header: BlockHeader, parents_hash: Vec) -> Self { + pub fn new(block_header: BlockHeader) -> Self { Self { + parents_hash: block_header.parents_hash().expect("dag block must have parents hash"), block_header, - parents_hash, } } pub fn new_genesis(genesis_header: BlockHeader) -> DagHeader { @@ -31,6 +31,10 @@ impl DagHeader { parents_hash: vec![Hash::new(ORIGIN)], } } + + pub fn number(&self) -> BlockNumber { + self.block_header.number() + } } impl Into for DagHeader { diff --git a/types/src/startup_info.rs b/types/src/startup_info.rs index 8ff3d94dc8..fc731a580f 100644 --- a/types/src/startup_info.rs +++ b/types/src/startup_info.rs @@ -122,16 +122,13 @@ pub struct ChainStatus { pub head: BlockHeader, /// Chain block info pub info: BlockInfo, - /// tips of the dag chain in dag accumulator snapshots - pub tips_hash: Option>, } impl ChainStatus { - pub fn new(head: BlockHeader, info: BlockInfo, tips_hash: Option>) -> Self { + pub fn new(head: BlockHeader, info: BlockInfo) -> Self { Self { head, info, - tips_hash, } } @@ -156,7 +153,6 @@ impl ChainStatus { Self { head: head.clone(), info: block_info, - tips_hash: Some(vec![head.id()]), } } @@ -175,14 +171,6 @@ impl ChainStatus { pub fn into_inner(self) -> (BlockHeader, BlockInfo) { (self.head, self.info) } - - pub fn get_last_tip_block_id(&self) -> Option { - if let Some(tips) = &self.tips_hash { - tips.into_iter().max().cloned() - } else { - None - } - } } impl Sample for ChainStatus { @@ -190,7 +178,6 @@ impl Sample for ChainStatus { Self { head: BlockHeader::sample(), info: BlockInfo::sample(), - tips_hash: Some(vec![HashValue::zero()]), } } } @@ -230,6 +217,9 @@ impl DagChainStatus { pub struct StartupInfo { /// main chain head block hash pub main: HashValue, + + /// dag accumulator info hash + pub dag_main: Option } impl fmt::Display for StartupInfo { @@ -243,7 +233,17 @@ impl fmt::Display for StartupInfo { impl StartupInfo { pub fn new(main: HashValue) -> Self { - Self { main } + Self { + main, + dag_main: None, + } + } + + pub fn new_with_dag(main: HashValue, dag_main: Option) -> Self { + Self { + main, + dag_main, + } } pub fn update_main(&mut self, new_head: HashValue) { @@ -253,6 +253,16 @@ impl StartupInfo { pub fn get_main(&self) -> &HashValue { &self.main } + + pub fn update_dag_main(&mut self, new_head: HashValue) { + self.dag_main = Some(new_head); + } + + pub fn get_dag_main(&self) -> Option { + self.dag_main + } + + } impl TryFrom> for StartupInfo { diff --git a/types/src/system_events.rs b/types/src/system_events.rs index 778bbc065e..6221778ab4 100644 --- a/types/src/system_events.rs +++ b/types/src/system_events.rs @@ -14,7 +14,7 @@ pub struct NewHeadBlock(pub Arc, pub Option>); /// may be uncle block #[derive(Clone, Debug)] -pub struct NewBranch(pub Arc, pub Option>); +pub struct NewBranch(pub Arc); #[derive(Clone, Debug)] pub struct MinedBlock(pub Arc, pub Option>); From 71c4a18918d98be0050acf44dad929d05f2316d7 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Fri, 10 Nov 2023 14:00:54 +0800 Subject: [PATCH 15/17] add sync for dag accumulator --- block-relayer/src/block_relayer.rs | 6 +- chain/api/src/chain.rs | 5 +- chain/service/src/chain_service.rs | 83 +++-- chain/src/chain.rs | 54 ++- chain/src/verifier/mod.rs | 82 ++--- consensus/src/dag/blockdag.rs | 101 ++++-- flexidag/src/flexidag_service.rs | 337 ++++++++++++++---- miner/src/create_block_template/mod.rs | 9 +- network-rpc/api/src/dag_protocol.rs | 2 - network/api/src/messages.rs | 2 - network/src/service.rs | 11 +- network/types/src/peer_info.rs | 2 - node/src/node.rs | 6 +- rpc/api/src/types.rs | 4 +- storage/src/flexi_dag/mod.rs | 37 +- storage/src/lib.rs | 25 +- sync/api/src/lib.rs | 5 +- .../block_connector_service.rs | 15 +- .../block_connector/test_write_block_chain.rs | 4 +- .../test_write_dag_block_chain.rs | 8 +- sync/src/block_connector/write_block_chain.rs | 123 +++---- sync/src/sync.rs | 98 +++-- sync/src/tasks/block_sync_task.rs | 194 +++------- sync/src/tasks/mod.rs | 41 +-- sync/src/tasks/sync_dag_block_task.rs | 13 +- sync/src/tasks/tests.rs | 2 +- sync/src/verified_rpc_client.rs | 9 +- test-helper/src/network.rs | 2 +- types/src/block.rs | 38 +- types/src/dag_block.rs | 17 + types/src/header.rs | 4 +- types/src/startup_info.rs | 29 +- types/src/system_events.rs | 2 +- 33 files changed, 760 insertions(+), 610 deletions(-) diff --git a/block-relayer/src/block_relayer.rs b/block-relayer/src/block_relayer.rs index 168c902c77..33baf7b34d 100644 --- a/block-relayer/src/block_relayer.rs +++ b/block-relayer/src/block_relayer.rs @@ -241,11 +241,7 @@ impl BlockRelayer { ) .await?; - block_connector_service.notify(PeerNewBlock::new( - peer_id, - block, - compact_block_msg.message.tips_hash, - ))?; + block_connector_service.notify(PeerNewBlock::new(peer_id, block))?; } Ok(()) }; diff --git a/chain/api/src/chain.rs b/chain/api/src/chain.rs index 8e872f5315..7943c64919 100644 --- a/chain/api/src/chain.rs +++ b/chain/api/src/chain.rs @@ -116,10 +116,7 @@ pub trait ChainWriter { fn connect(&mut self, executed_block: ExecutedBlock) -> Result; /// Verify, Execute and Connect block to current chain. - fn apply( - &mut self, - block: Block, - ) -> Result; + fn apply(&mut self, block: Block) -> Result; fn chain_state(&mut self) -> &ChainStateDB; } diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index 1b61e0c4ec..4df9f0581e 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -1,7 +1,7 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::{bail, format_err, Error, Result, Ok}; +use anyhow::{bail, format_err, Error, Ok, Result}; use starcoin_accumulator::node::AccumulatorStoreType; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::BlockChain; @@ -12,8 +12,10 @@ use starcoin_chain_api::{ use starcoin_config::NodeConfig; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; -use starcoin_flexidag::{FlexidagService, flexidag_service}; -use starcoin_flexidag::flexidag_service::{GetDagAccumulatorLeafDetail, UpdateDagTips, GetDagBlockParents}; +use starcoin_flexidag::flexidag_service::{ + GetDagAccumulatorLeafDetail, GetDagBlockParents, UpdateDagTips, +}; +use starcoin_flexidag::{flexidag_service, FlexidagService}; use starcoin_logger::prelude::*; use starcoin_network_rpc_api::dag_protocol::{ GetDagAccumulatorLeaves, GetTargetDagAccumulatorLeafDetail, TargetDagAccumulatorLeaf, @@ -25,6 +27,7 @@ use starcoin_service_registry::{ use starcoin_storage::{BlockStore, Storage, Store}; use starcoin_types::block::ExecutedBlock; use starcoin_types::contract_event::ContractEventInfo; +use starcoin_types::dag_block::KTotalDifficulty; use starcoin_types::filter::Filter; use starcoin_types::system_events::NewHeadBlock; use starcoin_types::transaction::RichTransactionInfo; @@ -93,8 +96,7 @@ impl EventHandler for ChainReaderService { let new_head = event.0.block().header().clone(); if let Err(e) = if self.inner.get_main().can_connect(event.0.as_ref()) { match self.inner.update_chain_head(event.0.as_ref().clone()) { - // wait for fixing: update_dag_accumulator should be in BlockChain - std::result::Result::Ok(_) => self.inner.update_dag_accumulator(new_head), + std::result::Result::Ok(_) => (), Err(e) => Err(e), } } else { @@ -266,12 +268,14 @@ impl ServiceHandler for ChainReaderService { ChainRequest::GetTargetDagAccumulatorLeafDetail { leaf_index, batch_size, - } => { - Ok(ChainResponse::TargetDagAccumulatorLeafDetail(self.inner.get_target_dag_accumulator_leaf_detail(GetTargetDagAccumulatorLeafDetail { - leaf_index, - batch_size, - })?)) - }, + } => Ok(ChainResponse::TargetDagAccumulatorLeafDetail( + self.inner.get_target_dag_accumulator_leaf_detail( + GetTargetDagAccumulatorLeafDetail { + leaf_index, + batch_size, + }, + )?, + )), } } } @@ -335,6 +339,11 @@ impl ChainReaderServiceInner { pub fn update_dag_accumulator(&mut self, new_block_header: BlockHeader) -> Result<()> { async_std::task::block_on(self.flexidag_service.send(UpdateDagTips { block_header: new_block_header, + current_head_block_id: self.main.status().info().id(), + k_total_difficulty: KTotalDifficulty { + head_block_id: self.main.status().info().id(), + total_difficulty: self.main.status().info().get_total_difficulty(), + }, }))? } } @@ -357,11 +366,12 @@ impl ReadableChainService for ChainReaderServiceInner { .into_iter() .map(|block| { if let Some(block) = block { - let result_parents = async_std::task::block_on(self.flexidag_service.send(GetDagBlockParents { - block_id: block.id(), - })).expect("failed to get the dag block parents"); - let parents = match result_parents - { + let result_parents = + async_std::task::block_on(self.flexidag_service.send(GetDagBlockParents { + block_id: block.id(), + })) + .expect("failed to get the dag block parents"); + let parents = match result_parents { std::result::Result::Ok(parents) => parents.parents, Err(_) => panic!("failed to get parents of block {}", block.id()), }; @@ -510,32 +520,37 @@ impl ReadableChainService for ChainReaderServiceInner { &self, req: GetDagAccumulatorLeaves, ) -> anyhow::Result> { - Ok(async_std::task::block_on(self.flexidag_service.send(flexidag_service::GetDagAccumulatorLeaves { - leaf_index: req.accumulator_leaf_index, - batch_size: req.batch_size, - reverse: true, - }))??.into_iter().map(|leaf| { - TargetDagAccumulatorLeaf { - accumulator_root: leaf.dag_accumulator_root, - leaf_index: leaf.leaf_index, - } - }).collect()) + Ok(async_std::task::block_on(self.flexidag_service.send( + flexidag_service::GetDagAccumulatorLeaves { + leaf_index: req.accumulator_leaf_index, + batch_size: req.batch_size, + reverse: true, + }, + ))?? + .into_iter() + .map(|leaf| TargetDagAccumulatorLeaf { + accumulator_root: leaf.dag_accumulator_root, + leaf_index: leaf.leaf_index, + }) + .collect()) } fn get_target_dag_accumulator_leaf_detail( &self, req: GetTargetDagAccumulatorLeafDetail, ) -> anyhow::Result> { - let dag_details = async_std::task::block_on(self.flexidag_service.send(GetDagAccumulatorLeafDetail { - leaf_index: req.leaf_index, - batch_size: req.batch_size, - }))??; - Ok(dag_details.into_iter().map(|detail| { - TargetDagAccumulatorLeafDetail { + let dag_details = + async_std::task::block_on(self.flexidag_service.send(GetDagAccumulatorLeafDetail { + leaf_index: req.leaf_index, + batch_size: req.batch_size, + }))??; + Ok(dag_details + .into_iter() + .map(|detail| TargetDagAccumulatorLeafDetail { accumulator_root: detail.accumulator_root, tips: detail.tips, - } - }).collect()) + }) + .collect()) } } diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 59078ce555..51f8147297 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -29,6 +29,7 @@ use starcoin_storage::Store; use starcoin_time_service::TimeService; use starcoin_types::block::BlockIdAndNumber; use starcoin_types::contract_event::ContractEventInfo; +use starcoin_types::dag_block::KTotalDifficulty; use starcoin_types::filter::Filter; use starcoin_types::header::DagHeader; use starcoin_types::startup_info::{ChainInfo, ChainStatus}; @@ -47,6 +48,7 @@ use starcoin_vm_types::effects::Op; use starcoin_vm_types::genesis_config::ConsensusStrategy; use starcoin_vm_types::on_chain_resource::Epoch; use std::cmp::min; +use std::collections::BTreeSet; use std::iter::Extend; use std::option::Option::{None, Some}; use std::{collections::HashMap, sync::Arc}; @@ -192,6 +194,13 @@ impl BlockChain { .expect("failed to calculate the dag key"), new_tips, dag_accumulator.get_info(), + genesis_id, + [KTotalDifficulty { + head_block_id: genesis_id, + total_difficulty: executed_block.block_info().get_total_difficulty(), + }] + .into_iter() + .collect(), )?; Self::new(time_service, executed_block.block.id(), storage, net, None) } @@ -378,10 +387,7 @@ impl BlockChain { V::verify_block(self, block) } - pub fn apply_with_verifier( - &mut self, - block: Block, - ) -> Result + pub fn apply_with_verifier(&mut self, block: Block) -> Result where V: BlockVerifier, { @@ -393,18 +399,12 @@ impl BlockChain { } //TODO remove this function. - pub fn update_chain_head( - &mut self, - block: Block, - ) -> Result { + pub fn update_chain_head(&mut self, block: Block) -> Result { let block_info = self .storage .get_block_info(block.id())? .ok_or_else(|| format_err!("Can not find block info by hash {:?}", block.id()))?; - self.connect(ExecutedBlock { - block, - block_info, - }) + self.connect(ExecutedBlock { block, block_info }) } //TODO consider move this logic to BlockExecutor @@ -572,10 +572,7 @@ impl BlockChain { storage.save_table_infos(txn_table_infos)?; watch(CHAIN_WATCH_NAME, "n26"); - Ok(ExecutedBlock { - block, - block_info, - }) + Ok(ExecutedBlock { block, block_info }) } pub fn get_txn_accumulator(&self) -> &MerkleAccumulator { @@ -593,12 +590,10 @@ impl ChainReader for BlockChain { self.status.head.header().chain_id(), self.genesis_hash, self.status.status.clone(), - self.storage - .get_dag_accumulator_info() - .expect(&format!( - "the dag accumulator info cannot be found by id: {}", - self.status.head.header().id() - )), + self.storage.get_dag_accumulator_info().expect(&format!( + "the dag accumulator info cannot be found by id: {}", + self.status.head.header().id() + )), ) } @@ -607,10 +602,7 @@ impl ChainReader for BlockChain { } fn head_block(&self) -> ExecutedBlock { - ExecutedBlock::new( - self.status.head.clone(), - self.status.status.info.clone(), - ) + ExecutedBlock::new(self.status.head.clone(), self.status.status.info.clone()) } fn current_header(&self) -> BlockHeader { @@ -1082,10 +1074,7 @@ impl ChainWriter for BlockChain { self.statedb = ChainStateDB::new(self.storage.clone().into_super_arc(), Some(state_root)); self.status = ChainStatusWithBlock { - status: ChainStatus::new( - block.header().clone(), - block_info.clone(), - ), + status: ChainStatus::new(block.header().clone(), block_info.clone()), head: block.clone(), }; if self.epoch.end_block_number() == block.header().number() { @@ -1100,10 +1089,7 @@ impl ChainWriter for BlockChain { Ok(executed_block) } - fn apply( - &mut self, - block: Block, - ) -> Result { + fn apply(&mut self, block: Block) -> Result { self.apply_with_verifier::(block) } diff --git a/chain/src/verifier/mod.rs b/chain/src/verifier/mod.rs index e398bee747..eb8ed93d29 100644 --- a/chain/src/verifier/mod.rs +++ b/chain/src/verifier/mod.rs @@ -184,49 +184,49 @@ impl BlockVerifier for BasicVerifier { // dag // jacktest: TODO: the verifying should be modified!!! // if chain_status.tips_hash.is_some() { - // let mut tips_hash = chain_status.tips_hash.clone().unwrap(); - // tips_hash.sort(); - - // if it is a dag block - // if HashValue::sha3_256_of(&tips_hash.encode().expect("hash encode must be successful")) - // != new_block_parent - // { - // // or a block of a single chain - // verify_block!( - // VerifyBlockField::Header, - // expect_number == new_block_header.number(), - // "Invalid block: Unexpect block number, expect:{}, got: {}.", - // expect_number, - // new_block_header.number() - // ); - - // verify_block!( - // VerifyBlockField::Header, - // current_id == new_block_parent, - // "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", - // current_id, - // new_block_parent, - // new_block_header.number() - // ); - // } + // let mut tips_hash = chain_status.tips_hash.clone().unwrap(); + // tips_hash.sort(); + + // if it is a dag block + // if HashValue::sha3_256_of(&tips_hash.encode().expect("hash encode must be successful")) + // != new_block_parent + // { + // // or a block of a single chain + // verify_block!( + // VerifyBlockField::Header, + // expect_number == new_block_header.number(), + // "Invalid block: Unexpect block number, expect:{}, got: {}.", + // expect_number, + // new_block_header.number() + // ); + + // verify_block!( + // VerifyBlockField::Header, + // current_id == new_block_parent, + // "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", + // current_id, + // new_block_parent, + // new_block_header.number() + // ); + // } // } else { - // or a block of a single chain - verify_block!( - VerifyBlockField::Header, - expect_number == new_block_header.number(), - "Invalid block: Unexpect block number, expect:{}, got: {}.", - expect_number, - new_block_header.number() - ); + // or a block of a single chain + verify_block!( + VerifyBlockField::Header, + expect_number == new_block_header.number(), + "Invalid block: Unexpect block number, expect:{}, got: {}.", + expect_number, + new_block_header.number() + ); - verify_block!( - VerifyBlockField::Header, - current_id == new_block_parent, - "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", - current_id, - new_block_parent, - new_block_header.number() - ); + verify_block!( + VerifyBlockField::Header, + current_id == new_block_parent, + "Invalid block: Parent id mismatch, expect:{}, got: {}, number:{}.", + current_id, + new_block_parent, + new_block_header.number() + ); // } verify_block!( VerifyBlockField::Header, diff --git a/consensus/src/dag/blockdag.rs b/consensus/src/dag/blockdag.rs index 728f76c5cd..a50b4459fb 100644 --- a/consensus/src/dag/blockdag.rs +++ b/consensus/src/dag/blockdag.rs @@ -1,7 +1,6 @@ use super::ghostdag::protocol::{ColoringOutput, GhostdagManager}; use super::reachability::{inquirer, reachability_service::MTReachabilityService}; use super::types::ghostdata::GhostdagData; -use crate::FlexiDagStorageConfig; use crate::consensusdb::prelude::StoreError; use crate::consensusdb::schemadb::GhostdagStoreReader; use crate::consensusdb::{ @@ -11,25 +10,26 @@ use crate::consensusdb::{ HeaderStore, ReachabilityStoreReader, RelationsStore, RelationsStoreReader, }, }; +use crate::FlexiDagStorageConfig; use anyhow::{anyhow, bail, Ok}; use bcs_ext::BCSCodec; use parking_lot::RwLock; use starcoin_accumulator::accumulator_info::AccumulatorInfo; -use starcoin_accumulator::{MerkleAccumulator, Accumulator}; use starcoin_accumulator::node::AccumulatorStoreType; +use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_config::NodeConfig; use starcoin_crypto::HashValue as Hash; -use starcoin_storage::flexi_dag::SyncFlexiDagSnapshot; +use starcoin_storage::flexi_dag::{KTotalDifficulty, SyncFlexiDagSnapshot, SyncFlexiDagSnapshotHasher}; use starcoin_storage::storage::CodecKVStore; -use starcoin_storage::{Store, SyncFlexiDagStore, Storage, BlockStore}; +use starcoin_storage::{BlockStore, Storage, Store, SyncFlexiDagStore}; use starcoin_types::block::BlockNumber; use starcoin_types::startup_info; use starcoin_types::{ blockhash::{BlockHashes, KType, ORIGIN}, header::{ConsensusHeader, DagHeader}, }; -use std::collections::HashMap; use std::collections::HashSet; +use std::collections::{BinaryHeap, HashMap}; use std::path::Path; use std::sync::{Arc, Mutex}; @@ -90,41 +90,93 @@ impl BlockDAG { dag } - pub fn try_init_with_storage(storage: Arc, config: Arc) -> anyhow::Result<(Option, Option)> { - let startup_info = storage.get_startup_info()?.expect("startup info must exist"); + pub fn calculate_dag_accumulator_key(snapshot: &SyncFlexiDagSnapshotHasher) -> anyhow::Result { + Ok(Hash::sha3_256_of(&snapshot.encode().expect( + "encoding the sorted relatship set must be successful", + ))) + } + + pub fn try_init_with_storage( + storage: Arc, + config: Arc, + ) -> anyhow::Result<(Option, Option)> { + let startup_info = storage + .get_startup_info()? + .expect("startup info must exist"); if let Some(key) = startup_info.get_dag_main() { - let accumulator_info = storage.get_dag_accumulator_info()?.expect("dag accumulator info should exist"); - assert!(accumulator_info.get_num_leaves() > 0, "the number of dag accumulator leaf must be greater than 0"); + let accumulator_info = storage + .get_dag_accumulator_info()? + .expect("dag accumulator info should exist"); + assert!( + accumulator_info.get_num_leaves() > 0, + "the number of dag accumulator leaf must be greater than 0" + ); let dag_accumulator = MerkleAccumulator::new_with_info( accumulator_info, storage.get_accumulator_store(AccumulatorStoreType::SyncDag), ); - let dag_genesis_hash = dag_accumulator.get_leaf(0)?.expect("the genesis in dag accumulator must none be none"); - - let dag_genesis_header = storage.get_block_header_by_hash(dag_genesis_hash)?.expect("the genesis block in dag accumulator must none be none"); + let dag_genesis_hash = dag_accumulator + .get_leaf(0)? + .expect("the genesis in dag accumulator must none be none"); - Ok((Some(Self::new_by_config(DagHeader::new_genesis(dag_genesis_header), config.data_dir().join("flexidag").as_path())?), Some(dag_accumulator))) + let dag_genesis_header = storage + .get_block_header_by_hash(dag_genesis_hash)? + .expect("the genesis block in dag accumulator must none be none"); + Ok(( + Some(Self::new_by_config( + DagHeader::new_genesis(dag_genesis_header), + config.data_dir().join("flexidag").as_path(), + )?), + Some(dag_accumulator), + )) } else { - let block_header = storage.get_block_header_by_hash(startup_info.get_main().clone())?.expect("the genesis block in dag accumulator must none be none"); + let block_header = storage + .get_block_header_by_hash(startup_info.get_main().clone())? + .expect("the genesis block in dag accumulator must none be none"); let fork_height = storage.dag_fork_height(config.net().id().clone()); if block_header.number() < fork_height { Ok((None, None)) } else if block_header.number() == fork_height { - let dag_accumulator = MerkleAccumulator::new_with_info(AccumulatorInfo::default(), storage.get_accumulator_store(AccumulatorStoreType::SyncDag)); - dag_accumulator.append(&[block_header.id()])?; - storage.get_accumulator_snapshot_storage().put(Self::calculate_dag_accumulator_key(vec![block_header.id()])?, SyncFlexiDagSnapshot { + let dag_accumulator = MerkleAccumulator::new_with_info( + AccumulatorInfo::default(), + storage.get_accumulator_store(AccumulatorStoreType::SyncDag), + ); + + + let k_total_difficulties = BinaryHeap::new(); + k_total_difficulties.push(KTotalDifficulty { + head_block_id: block_header.id(), + total_difficulty: storage + .get_block_info(block_header.id())? + .expect("block info must exist") + .get_total_difficulty(), + }); + let snapshot_hasher = SyncFlexiDagSnapshotHasher { child_hashes: vec![block_header.id()], - accumulator_info: dag_accumulator.get_info(), head_block_id: block_header.id(), - })?; - Ok((Some(Self::new_by_config(DagHeader::new_genesis(block_header), config.data_dir().join("flexidag").as_path())?), Some(dag_accumulator))) + k_total_difficulties, + }; + let key = Self::calculate_dag_accumulator_key(&snapshot_hasher)?; + dag_accumulator.append(&[key])?; + storage.get_accumulator_snapshot_storage().put( + key, + snapshot_hasher.to_snapshot(dag_accumulator.get_info()), + )?; + dag_accumulator.flush()?; + Ok(( + Some(Self::new_by_config( + DagHeader::new_genesis(block_header), + config.data_dir().join("flexidag").as_path(), + )?), + Some(dag_accumulator), + )) } else { bail!("failed to init dag") } } } - + pub fn new_by_config(header: DagHeader, db_path: &Path) -> anyhow::Result { let config = FlexiDagStorageConfig::create_with_params(1, 0, 1024); let db = FlexiDagStorage::create_from_path(db_path, config)?; @@ -132,13 +184,6 @@ impl BlockDAG { Ok(dag) } - pub fn calculate_dag_accumulator_key(mut tips: Vec) -> anyhow::Result { - tips.sort(); - Ok(Hash::sha3_256_of(&tips.encode().expect( - "encoding the sorted relatship set must be successful", - ))) - } - pub fn clear_missing_block(&mut self) { self.missing_blocks.clear(); } diff --git a/flexidag/src/flexidag_service.rs b/flexidag/src/flexidag_service.rs index 84e78050f3..8966d0340e 100644 --- a/flexidag/src/flexidag_service.rs +++ b/flexidag/src/flexidag_service.rs @@ -1,18 +1,28 @@ -use std::sync::Arc; - -use anyhow::{anyhow, Result, Ok, bail, Error}; -use starcoin_accumulator::{Accumulator, MerkleAccumulator, accumulator_info::AccumulatorInfo}; -use starcoin_config::NodeConfig; -use starcoin_consensus::{BlockDAG, dag::types::ghostdata::GhostdagData}; +use std::{ + collections::{BTreeSet, BinaryHeap}, + sync::Arc, +}; + +use anyhow::{anyhow, bail, Error, Ok, Result}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_config::{NodeConfig, TimeService}; +use starcoin_consensus::{dag::types::ghostdata::GhostdagData, BlockDAG}; use starcoin_crypto::HashValue; -use starcoin_service_registry::{ActorService, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest}; -use starcoin_storage::{storage::CodecKVStore, flexi_dag::SyncFlexiDagSnapshot, Storage, SyncFlexiDagStore, BlockStore}; -use starcoin_types::{block::BlockHeader, startup_info, header::DagHeader}; +use starcoin_service_registry::{ + ActorService, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest, +}; +use starcoin_storage::{ + flexi_dag::{KTotalDifficulty, SyncFlexiDagSnapshot, SyncFlexiDagSnapshotHasher}, + storage::CodecKVStore, + BlockStore, Storage, SyncFlexiDagStore, block_info::BlockInfoStore, +}; +use starcoin_types::{block::BlockHeader, header::DagHeader, startup_info}; #[derive(Debug, Clone)] pub struct DumpTipsToAccumulator { pub block_header: BlockHeader, pub current_head_block_id: HashValue, + pub k_total_difficulty: KTotalDifficulty, } impl ServiceRequest for DumpTipsToAccumulator { @@ -23,6 +33,7 @@ impl ServiceRequest for DumpTipsToAccumulator { pub struct UpdateDagTips { pub block_header: BlockHeader, pub current_head_block_id: HashValue, + pub k_total_difficulty: KTotalDifficulty, } impl ServiceRequest for UpdateDagTips { @@ -105,25 +116,36 @@ impl ServiceRequest for AddToDag { type Response = anyhow::Result; } +#[derive(Debug, Clone)] +pub struct ForkDagAccumulator { + pub new_blocks: Vec, + pub dag_accumulator_index: u64, + pub block_header_id: HashValue, +} + +impl ServiceRequest for ForkDagAccumulator { + type Response = anyhow::Result; +} + +pub struct TipInfo { + tips: Option>, // some is for dag or the state of the chain is still in old version + k_total_difficulties: BTreeSet, +} pub struct FlexidagService { dag: Option, dag_accumulator: Option, - tips: Option>, // some is for dag or the state of the chain is still in old version + tip_info: Option, storage: Arc, } impl FlexidagService { - pub fn add_to_dag( - &mut self, - header: BlockHeader, - ) -> Result> { + pub fn add_to_dag(&mut self, header: BlockHeader) -> Result> { let dag = match &mut self.dag { Some(dag) => dag, None => bail!("dag is none"), }; - match dag.get_ghostdag_data(header.id()) - { + match dag.get_ghostdag_data(header.id()) { std::result::Result::Ok(ghost_dag_data) => Ok(ghost_dag_data), Err(_) => std::result::Result::Ok(Arc::new( // jacktest: TODO:add_to_dag should not use parents hash since the block header has them @@ -131,30 +153,115 @@ impl FlexidagService { )), } } + + fn create_snapshot_by_tips(tips: Vec, head_block_id: HashValue) -> Result<(HashValue, SyncFlexiDagSnapshotHasher)> { + let k_total_difficulties = BTreeSet::new(); + tips.iter().for_each(|block_id| { + k_total_difficulties.insert(KTotalDifficulty { + head_block_id: block_id.clone(), + total_difficulty: self.storage.get_block_info(block_id.clone()).expect("block info should not be none").ok_or_else(error || anyhow!("block info should not be none"))?.total_difficulty, + }); + }); + + let snaphot_hasher = SyncFlexiDagSnapshotHasher { + child_hashes: tips, + head_block_id, + k_total_difficulties, + }; + + Ok((BlockDAG::calculate_dag_accumulator_key(&snapshot_hasher)?, snaphot_hasher)) + } + + fn merge_from_big_dag(&mut self, msg: ForkDagAccumulator) -> Result { + let dag_accumulator = self.dag_accumulator.as_mut().ok_or_else("the dag accumulator should not be none")?; + if dag_accumulator.num_leaves() != msg.dag_accumulator_index { + bail!("cannot merge dag accumulator since its number is not the same as other"); + } + let tip_info = self.tip_info.as_mut().ok_or_else("the tips should not be none")?; + msg.new_blocks.iter().for_each(|block_id| { + if !tip_info.tips.contains(block_id) { + tip_info.tips.push(block_id.clone()); + } + }); + + let (key, snaphot_hasher) = Self::create_snapshot_by_tips(tip_info.tips, msg.block_header_id)?; + dag_accumulator.append(&vec![key])?; + let dag_accumulator_info = dag_accumulator.get_info(); + self.storage.get_accumulator_snapshot_storage().put(key, snaphot_hasher.to_snapshot(dag_accumulator_info))?; + dag_accumulator.flush()?; + Ok(dag_accumulator_info) + } + + fn merge_from_small_dag(&mut self, msg: ForkDagAccumulator) -> Result { + let dag_accumulator = self + .dag_accumulator + .as_mut() + .ok_or_else(error || anyhow!("dag accumulator is none"))?; + // fetch the block in the dag according to the dag accumulator index + let previous_key = dag_accumulator.get_leaf(msg.dag_accumulator_index - 1)? + .ok_or_else(error || anyhow!("the dag snapshot hash is none"))?; + + let current_key = dag_accumulator.get_leaf(msg.dag_accumulator_index)? + .ok_or_else(error || anyhow!("the dag snapshot hash is none"))?; + + let pre_snapshot = self + .storage + .get_accumulator_snapshot_storage() + .get(previous_key)? + .ok_or_else(error || anyhow!("the dag snapshot is none"))?; + + let current_snapshot = self + .storage + .get_accumulator_snapshot_storage() + .get(current_key)? + .ok_or_else(error || anyhow!("the dag snapshot is none"))?; + + // fork the dag accumulator according to the ForkDagAccumulator.dag_accumulator_index + let fork = dag_accumulator.fork(Some(pre_snapshot.accumulator_info)); + + let mut new_blocks = msg.new_blocks; + current_snapshot.child_hashes.iter().for_each(|block_id| { + if !new_blocks.contains(block_id) { + new_blocks.push(block_id.clone()); + } + }); + + let (key, snaphot_hasher) = Self::create_snapshot_by_tips(new_blocks, msg.block_header_id)?; + fork.append(&vec![key])?; + let dag_accumulator_info = fork.get_info(); + self.storage.get_accumulator_snapshot_storage().put(key, snaphot_hasher.to_snapshot(dag_accumulator_info))?; + fork.flush()?; + Ok(dag_accumulator_info) + } + } impl ServiceFactory for FlexidagService { fn create(ctx: &mut ServiceContext) -> Result { let storage = ctx.get_shared::>()?; let config = ctx.get_shared::>()?; - let (dag, dag_accumulator) = BlockDAG::try_init_with_storage(storage.clone(), config.clone())?; - let tips = dag_accumulator.as_ref().map(|accumulator| { + let (dag, dag_accumulator) = + BlockDAG::try_init_with_storage(storage.clone(), config.clone())?; + let tip_info = dag_accumulator.as_ref().map(|accumulator| { let tips_index = accumulator.num_leaves(); let tips_key = accumulator .get_leaf(tips_index) .expect("failed to read the dag snapshot hash") .expect("the dag snapshot hash is none"); - storage + let snapshot = storage .get_accumulator_snapshot_storage() .get(tips_key) .expect("failed to read the snapsho object") - .expect("dag snapshot object is none") - .child_hashes + .expect("dag snapshot object is none"); + TipInfo { + tips: Some(snapshot.child_hashes), + k_total_difficulties: snapshot.k_total_difficulties, + } }); Ok(Self { dag, dag_accumulator, - tips, + tip_info, storage: storage.clone(), }) } @@ -172,11 +279,15 @@ impl ActorService for FlexidagService { } } -// send this message after minting a new block -// and the block was committed +// send this message after minting a new block +// and the block was committed // and startup info was updated impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: DumpTipsToAccumulator, ctx: &mut ServiceContext) -> Result<()> { + fn handle( + &mut self, + msg: DumpTipsToAccumulator, + ctx: &mut ServiceContext, + ) -> Result<()> { let storage = ctx.get_shared::>()?; if self.tips.is_none() { let config = ctx.get_shared::>()?; @@ -186,37 +297,66 @@ impl ServiceHandler for FlexidagService { } else { // initialize the dag data, the chain will be the dag chain at next block self.dag = dag; - self.tips = Some(vec![msg.block_header.id()]); self.dag_accumulator = dag_accumulator; - + self.tip_info = Some(TipInfo { + tips: Some(vec![msg.block_header.id()]), + k_total_difficulties: [msg.block_header.id()].into_iter().cloned().collect(), + }); + self.storage = storage.clone(); Ok(()) } } else { // the chain had became the flexidag chain - let tips = self.tips.take().expect("the tips should not be none in this branch"); + let tip_info = self + .tip_info + .take() + .expect("the tips should not be none in this branch"); let key = BlockDAG::calculate_dag_accumulator_key(tips.clone())?; - let dag = self.dag_accumulator.as_mut().expect("the tips is not none but the dag accumulator is none"); + let dag = self + .dag_accumulator + .as_mut() + .expect("the tips is not none but the dag accumulator is none"); dag.append(&vec![key])?; - storage.get_accumulator_snapshot_storage().put(key, SyncFlexiDagSnapshot { - child_hashes: tips, - accumulator_info: dag.get_info(), - head_block_id: msg.current_head_block_id, - })?; + storage.get_accumulator_snapshot_storage().put( + key, + SyncFlexiDagSnapshot { + child_hashes: tip_info.tips.expect("the tips should not be none"), + accumulator_info: dag.get_info(), + head_block_id: msg.current_head_block_id, + k_total_difficulties: tip_info + .k_total_difficulties + .into_iter() + .take(16) + .cloned() + .collect(), + }, + )?; dag.flush()?; - self.tips = Some(vec![msg.block_header.id()]); + self.tip_info = Some(TipInfo { + tips: Some(vec![msg.block_header.id()]), + k_total_difficulties: [msg.block_header.id()].into_iter().cloned().collect(), + }); + self.storage = storage.clone(); Ok(()) } } } impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: UpdateDagTips, ctx: &mut ServiceContext) -> Result<()> { + fn handle( + &mut self, + msg: UpdateDagTips, + ctx: &mut ServiceContext, + ) -> Result<()> { let header = msg.block_header; - match self.tips.clone() { - Some(mut tips) => { - if !tips.contains(&header.id()) { - tips.push(header.id()); - self.tips = Some(tips); + match &mut self.tip_info { + Some(tip_info) => { + if !tip_info.tips.contains(&header.id()) { + tip_info.tips.push(header.id()); + tip_info.k_total_difficulties.insert(KTotalDifficulty { + head_block_id: msg.k_total_difficulty.head_block_id, + total_difficulty: msg.k_total_difficulty.total_difficulty, + }); } Ok(()) } @@ -224,19 +364,29 @@ impl ServiceHandler for FlexidagService { let storage = ctx.get_shared::>()?; let config = ctx.get_shared::>()?; if header.number() == storage.dag_fork_height(config.net().id().clone()) { - let (dag, dag_accumulator) = BlockDAG::try_init_with_storage(storage.clone(), config)?; + let (dag, dag_accumulator) = + BlockDAG::try_init_with_storage(storage.clone(), config)?; if dag.is_none() { Ok(()) // the chain is still in single chain } else { // initialize the dag data, the chain will be the dag chain at next block self.dag = dag; - self.tips = Some(vec![header.id()]); + self.tip_info = Some(TipInfo { + tips: Some(vec![msg.block_header.id()]), + k_total_difficulties: [msg.block_header.id()] + .into_iter() + .cloned() + .collect(), + }); self.dag_accumulator = dag_accumulator; - storage.get_startup_info()?.map(|mut startup_info| { - startup_info.dag_main = Some(header.id()); - storage.save_startup_info(startup_info) - }).expect("starup info should not be none") + storage + .get_startup_info()? + .map(|mut startup_info| { + startup_info.dag_main = Some(header.id()); + storage.save_startup_info(startup_info) + }) + .expect("starup info should not be none") } } else { Ok(()) // drop the block, the chain is still in single chain @@ -247,21 +397,34 @@ impl ServiceHandler for FlexidagService { } impl ServiceHandler for FlexidagService { - fn handle(&mut self, _msg: GetDagTips, _ctx: &mut ServiceContext) -> Result>> { + fn handle( + &mut self, + _msg: GetDagTips, + _ctx: &mut ServiceContext, + ) -> Result>> { Ok(self.tips.clone()) } } impl ServiceHandler for FlexidagService { - fn handle(&mut self, _msg: GetDagAccumulatorInfo, _ctx: &mut ServiceContext) -> Result> { - Ok(self.dag_accumulator.as_ref().map(|dag_accumulator_info| { - dag_accumulator_info.get_info() - })) + fn handle( + &mut self, + _msg: GetDagAccumulatorInfo, + _ctx: &mut ServiceContext, + ) -> Result> { + Ok(self + .dag_accumulator + .as_ref() + .map(|dag_accumulator_info| dag_accumulator_info.get_info())) } } impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: GetDagAccumulatorLeaves, _ctx: &mut ServiceContext) -> Result> { + fn handle( + &mut self, + msg: GetDagAccumulatorLeaves, + _ctx: &mut ServiceContext, + ) -> Result> { match &self.dag_accumulator { Some(dag_accumulator) => { let end_index = std::cmp::min( @@ -275,8 +438,14 @@ impl ServiceHandler for FlexidagService { } else { index }; - let key = dag_accumulator.get_leaf(real_index)?.ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; - let snaptshot = self.storage.get_accumulator_snapshot_storage().get(key)?.expect("the snapshot should not be none"); + let key = dag_accumulator + .get_leaf(real_index)? + .ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; + let snaptshot = self + .storage + .get_accumulator_snapshot_storage() + .get(key)? + .expect("the snapshot should not be none"); result.push(DagAccumulatorLeaf { leaf_index: real_index, dag_accumulator_root: snaptshot.accumulator_info.accumulator_root, @@ -290,16 +459,26 @@ impl ServiceHandler for FlexidagService { } impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: GetDagBlockParents, _ctx: &mut ServiceContext) -> Result { + fn handle( + &mut self, + msg: GetDagBlockParents, + _ctx: &mut ServiceContext, + ) -> Result { match &self.dag { - Some(dag) => Ok(DagBlockParents { parents: dag.get_parents(msg.block_id)? } ) , + Some(dag) => Ok(DagBlockParents { + parents: dag.get_parents(msg.block_id)?, + }), None => bail!("dag is none"), } } } impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: GetDagAccumulatorLeafDetail, _ctx: &mut ServiceContext) -> Result> { + fn handle( + &mut self, + msg: GetDagAccumulatorLeafDetail, + _ctx: &mut ServiceContext, + ) -> Result> { match &self.dag_accumulator { Some(dag_accumulator) => { let end_index = std::cmp::min( @@ -307,10 +486,14 @@ impl ServiceHandler for FlexidagService { dag_accumulator.num_leaves() - 1, ); let mut details = vec![]; - let snapshot_storage = self.storage.get_accumulator_snapshot_storage(); + let snapshot_storage = self.storage.get_accumulator_snapshot_storage(); for index in msg.leaf_index..=end_index { - let key = dag_accumulator.get_leaf(index)?.ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; - let snapshot = snapshot_storage.get(key)?.ok_or_else(|| anyhow!("the dag snapshot is none"))?; + let key = dag_accumulator + .get_leaf(index)? + .ok_or_else(|| anyhow!("the dag snapshot hash is none"))?; + let snapshot = snapshot_storage + .get(key)? + .ok_or_else(|| anyhow!("the dag snapshot is none"))?; details.push(DagAccumulatorLeafDetail { accumulator_root: snapshot.accumulator_info.accumulator_root, tips: snapshot.child_hashes, @@ -324,11 +507,37 @@ impl ServiceHandler for FlexidagService { } impl ServiceHandler for FlexidagService { - fn handle(&mut self, msg: AddToDag, _ctx: &mut ServiceContext) -> Result { + fn handle( + &mut self, + msg: AddToDag, + _ctx: &mut ServiceContext, + ) -> Result { let ghost_dag_data = self.add_to_dag(msg.block_header)?; - Ok(MergesetBlues { + Ok(MergesetBlues { selected_parent: ghost_dag_data.selected_parent, - mergeset_blues: ghost_dag_data.mergeset_blues.as_ref().clone(), + mergeset_blues: ghost_dag_data.mergeset_blues.as_ref().clone(), }) } } + +impl ServiceHandler for FlexidagService { + fn handle( + &mut self, + msg: ForkDagAccumulator, + _ctx: &mut ServiceContext, + ) -> Result { + let dag_accumulator = self + .dag_accumulator + .as_ref() + .ok_or_else(error || anyhow!("dag accumulator is none"))?; + + if msg.dag_accumulator_index > dag_accumulator.num_leaves() { + self.merge_from_big_dag(msg) + } else { + self.merge_from_small_dag(msg) + } + + + // append the ForkDagAccumulator.new_blocks and the fetched blocks above into the forked dag accumulator + } +} diff --git a/miner/src/create_block_template/mod.rs b/miner/src/create_block_template/mod.rs index 4a91130f14..17764bcb83 100644 --- a/miner/src/create_block_template/mod.rs +++ b/miner/src/create_block_template/mod.rs @@ -14,11 +14,12 @@ use starcoin_consensus::Consensus; use starcoin_crypto::hash::HashValue; use starcoin_executor::VMMetrics; use starcoin_flexidag::flexidag_service::GetDagTips; -use starcoin_flexidag::{FlexidagService, flexidag_service}; +use starcoin_flexidag::{flexidag_service, FlexidagService}; use starcoin_logger::prelude::*; use starcoin_open_block::OpenedBlock; use starcoin_service_registry::{ - ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRequest, ServiceRef, + ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, ServiceRef, + ServiceRequest, }; use starcoin_storage::{BlockStore, Storage, Store}; use starcoin_txpool::TxPoolService; @@ -372,6 +373,8 @@ where } fn get_dag_block_parents(&self) -> Result>> { - Ok(async_std::task::block_on(self.flexidag_service.send(GetDagTips))??) + Ok(async_std::task::block_on( + self.flexidag_service.send(GetDagTips), + )??) } } diff --git a/network-rpc/api/src/dag_protocol.rs b/network-rpc/api/src/dag_protocol.rs index c47b28ac52..17b2936f7d 100644 --- a/network-rpc/api/src/dag_protocol.rs +++ b/network-rpc/api/src/dag_protocol.rs @@ -44,6 +44,4 @@ pub struct SyncDagBlockInfo { pub block_id: HashValue, pub block: Option, pub peer_id: Option, - pub dag_parents: Vec, - pub dag_transaction_header: Option, } diff --git a/network/api/src/messages.rs b/network/api/src/messages.rs index 053fa3a5c5..0e0de5351b 100644 --- a/network/api/src/messages.rs +++ b/network/api/src/messages.rs @@ -48,7 +48,6 @@ impl Sample for TransactionsMessage { pub struct CompactBlockMessage { pub compact_block: CompactBlock, pub block_info: BlockInfo, - pub tips_hash: Option>, } impl CompactBlockMessage { @@ -60,7 +59,6 @@ impl CompactBlockMessage { Self { compact_block, block_info, - tips_hash, } } } diff --git a/network/src/service.rs b/network/src/service.rs index cdad26aadb..15985eea9f 100644 --- a/network/src/service.rs +++ b/network/src/service.rs @@ -717,12 +717,11 @@ impl Inner { //2. Sync status change. // may be update by repeat message, but can not find a more good way. self.network_service.update_business_status( - ChainStatus::new( - msg.compact_block.header.clone(), - msg.block_info.clone(), - ) - .encode() - .expect("Encoding the compact_block.header and block_info must be successful"), + ChainStatus::new(msg.compact_block.header.clone(), msg.block_info.clone()) + .encode() + .expect( + "Encoding the compact_block.header and block_info must be successful", + ), ); self.self_peer.known_blocks.put(id, ()); diff --git a/network/types/src/peer_info.rs b/network/types/src/peer_info.rs index ca3c898301..13b0463afb 100644 --- a/network/types/src/peer_info.rs +++ b/network/types/src/peer_info.rs @@ -71,8 +71,6 @@ impl PeerInfo { pub fn update_dag_accumulator_info(&mut self, dag_accumulator_info: Option) { self.chain_info .update_dag_accumulator_info(dag_accumulator_info) - - } /// This peer is support notification diff --git a/node/src/node.rs b/node/src/node.rs index a15d0534f7..23d09bb621 100644 --- a/node/src/node.rs +++ b/node/src/node.rs @@ -224,11 +224,7 @@ impl ServiceHandler for NodeService { block_hash ) })?; - let result = connect_service - .send(ExecuteRequest { - block, - }) - .await??; + let result = connect_service.send(ExecuteRequest { block }).await??; info!("Re execute result: {:?}", result); Ok(()) }; diff --git a/rpc/api/src/types.rs b/rpc/api/src/types.rs index cfed8fab61..523be0cb14 100644 --- a/rpc/api/src/types.rs +++ b/rpc/api/src/types.rs @@ -23,7 +23,9 @@ use starcoin_crypto::{CryptoMaterialError, HashValue, ValidCryptoMaterialStringE use starcoin_resource_viewer::{AnnotatedMoveStruct, AnnotatedMoveValue}; use starcoin_service_registry::ServiceRequest; use starcoin_state_api::{StateProof, StateWithProof, StateWithTableItemProof}; -use starcoin_types::block::{Block, BlockBody, BlockHeader, BlockHeaderExtra, BlockInfo, BlockNumber, ParentsHash}; +use starcoin_types::block::{ + Block, BlockBody, BlockHeader, BlockHeaderExtra, BlockInfo, BlockNumber, ParentsHash, +}; use starcoin_types::contract_event::{ContractEvent, ContractEventInfo}; use starcoin_types::event::EventKey; use starcoin_types::genesis_config; diff --git a/storage/src/flexi_dag/mod.rs b/storage/src/flexi_dag/mod.rs index 6c86f98551..789dc31fe1 100644 --- a/storage/src/flexi_dag/mod.rs +++ b/storage/src/flexi_dag/mod.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::{ + collections::{BTreeSet, BinaryHeap}, + sync::Arc, +}; use crate::{ accumulator::{AccumulatorStorage, DagBlockAccumulatorStorage}, @@ -11,12 +14,44 @@ use bcs_ext::BCSCodec; use serde::{Deserialize, Serialize}; use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_crypto::HashValue; +use starcoin_types::dag_block::KTotalDifficulty; +use starcoin_uint::U256; #[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct SyncFlexiDagSnapshot { pub child_hashes: Vec, // child nodes(tips), to get the relationship, use dag's relationship store pub accumulator_info: AccumulatorInfo, pub head_block_id: HashValue, // to initialize the BlockInfo + pub k_total_difficulties: BTreeSet, // the k-th smallest total difficulty +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct SyncFlexiDagSnapshotHasher { + pub child_hashes: Vec, // child nodes(tips), to get the relationship, use dag's relationship store + pub head_block_id: HashValue, // to initialize the BlockInfo + pub k_total_difficulties: BTreeSet, // the k-th smallest total difficulty +} + +impl SyncFlexiDagSnapshotHasher { + pub fn to_snapshot(self, accumulator_info: AccumulatorInfo) -> SyncFlexiDagSnapshot { + SyncFlexiDagSnapshot { + child_hashes: self.child_hashes, + accumulator_info, + head_block_id: self.head_block_id, + k_total_difficulties: self.k_total_difficulties, + } + } +} + +impl From for SyncFlexiDagSnapshotHasher { + fn from(mut value: SyncFlexiDagSnapshot) -> Self { + value.child_hashes.sort(); + SyncFlexiDagSnapshotHasher { + child_hashes: value.child_hashes, + head_block_id: value.head_block_id, + k_total_difficulties: value.k_total_difficulties + } + } } impl ValueCodec for SyncFlexiDagSnapshot { diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 598b72d764..a3546ee7ec 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -21,7 +21,9 @@ use network_p2p_types::peer_id::PeerId; use num_enum::{IntoPrimitive, TryFromPrimitive}; use once_cell::sync::Lazy; use starcoin_accumulator::{ - accumulator_info::{AccumulatorInfo, self}, node::AccumulatorStoreType, AccumulatorTreeStore, + accumulator_info::{self, AccumulatorInfo}, + node::AccumulatorStoreType, + AccumulatorTreeStore, }; use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; @@ -31,8 +33,9 @@ use starcoin_types::{ block::{Block, BlockBody, BlockHeader, BlockInfo, BlockNumber}, blockhash::ORIGIN, contract_event::ContractEvent, + dag_block::KTotalDifficulty, header, - startup_info::{ChainInfo, ChainStatus, SnapshotRange, StartupInfo, self}, + startup_info::{self, ChainInfo, ChainStatus, SnapshotRange, StartupInfo}, transaction::{RichTransactionInfo, Transaction}, }; use starcoin_vm_types::{ @@ -41,7 +44,7 @@ use starcoin_vm_types::{ state_store::table::{TableHandle, TableInfo}, }; use std::{ - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, fmt::{Debug, Display, Formatter}, sync::Arc, }; @@ -330,6 +333,8 @@ pub trait SyncFlexiDagStore { key: HashValue, new_tips: Vec, accumulator_info: AccumulatorInfo, + head_block_id: HashValue, + k_total_difficulties: BTreeSet, ) -> Result<()>; fn get_dag_accumulator_info(&self) -> Result>; fn get_tips_by_block_id(&self, block_id: HashValue) -> Result>; @@ -682,19 +687,29 @@ impl SyncFlexiDagStore for Storage { let dag_main = dag_main.unwrap(); - Ok(Some(self.flexi_dag_storage.get_snapshot_storage().get(dag_main)?.expect("snapshot should not be none").accumulator_info)) + Ok(Some( + self.flexi_dag_storage + .get_snapshot_storage() + .get(dag_main)? + .expect("snapshot should not be none") + .accumulator_info, + )) } - // update dag accumulator + // update dag accumulator fn append_dag_accumulator_leaf( &self, key: HashValue, new_tips: Vec, accumulator_info: AccumulatorInfo, + head_block_id: HashValue, + k_total_difficulties: BTreeSet, ) -> Result<()> { let snapshot = SyncFlexiDagSnapshot { child_hashes: new_tips.clone(), accumulator_info: accumulator_info.clone(), + head_block_id, + k_total_difficulties, }; // for sync if let Some(t) = self.flexi_dag_storage.get_hashes_by_hash(key)? { diff --git a/sync/api/src/lib.rs b/sync/api/src/lib.rs index 5bb31fea85..60f4c869b2 100644 --- a/sync/api/src/lib.rs +++ b/sync/api/src/lib.rs @@ -31,10 +31,7 @@ pub struct PeerNewBlock { impl PeerNewBlock { pub fn new(peer_id: PeerId, new_block: Block) -> Self { - PeerNewBlock { - peer_id, - new_block, - } + PeerNewBlock { peer_id, new_block } } pub fn get_peer_id(&self) -> PeerId { diff --git a/sync/src/block_connector/block_connector_service.rs b/sync/src/block_connector/block_connector_service.rs index 52ea7ed771..7890f58fd9 100644 --- a/sync/src/block_connector/block_connector_service.rs +++ b/sync/src/block_connector/block_connector_service.rs @@ -3,6 +3,7 @@ #[cfg(test)] use super::CheckBlockConnectorHashValue; +use crate::block_connector::write_block_chain::ConnectOk; use crate::block_connector::{ExecuteRequest, ResetRequest, WriteBlockChainService}; use crate::sync::{CheckSyncEvent, SyncService}; use crate::tasks::{BlockConnectedEvent, BlockConnectedFinishEvent, BlockDiskCheckEvent}; @@ -12,8 +13,8 @@ use anyhow::{format_err, Ok, Result}; use network_api::PeerProvider; use starcoin_chain_api::{ChainReader, ConnectBlockError, WriteableChainService}; use starcoin_config::{NodeConfig, G_CRATE_VERSION}; -use starcoin_consensus::BlockDAG; use starcoin_consensus::dag::blockdag::InitDagState; +use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; use starcoin_flexidag::FlexidagService; @@ -22,7 +23,7 @@ use starcoin_network::NetworkServiceRef; use starcoin_service_registry::{ ActorService, EventHandler, ServiceContext, ServiceFactory, ServiceHandler, }; -use starcoin_storage::{BlockStore, Storage, flexi_dag}; +use starcoin_storage::{flexi_dag, BlockStore, Storage}; use starcoin_sync_api::PeerNewBlock; use starcoin_txpool::TxPoolService; use starcoin_txpool_api::TxPoolSyncService; @@ -268,7 +269,7 @@ where debug!("try connect mined block: {}", id); match self.chain_service.try_connect(block) { - std::result::Result::Ok(_) => { + std::result::Result::Ok(ConnectOk::DagConnected) => { match self.chain_service.dump_tips(block_header) { std::result::Result::Ok(_) => (), Err(e) => error!("failed to dump tips to dag accumulator: {}", e), @@ -302,15 +303,13 @@ where return; } let peer_id = msg.get_peer_id(); - if let Err(e) = self - .chain_service - .try_connect(msg.get_block().clone()) - { + if let Err(e) = self.chain_service.try_connect(msg.get_block().clone()) { match e.downcast::() { std::result::Result::Ok(connect_error) => { match connect_error { ConnectBlockError::FutureBlock(block) => { - self.chain_service.update_tips(msg.get_block().header().clone())?; + self.chain_service + .update_tips(msg.get_block().header().clone())?; //TODO cache future block if let std::result::Result::Ok(sync_service) = ctx.service_ref::() diff --git a/sync/src/block_connector/test_write_block_chain.rs b/sync/src/block_connector/test_write_block_chain.rs index 39401352c3..73b78a3dfa 100644 --- a/sync/src/block_connector/test_write_block_chain.rs +++ b/sync/src/block_connector/test_write_block_chain.rs @@ -164,9 +164,7 @@ fn gen_fork_block_chain( .unwrap(); parent_id = block.id(); - writeable_block_chain_service - .try_connect(block) - .unwrap(); + writeable_block_chain_service.try_connect(block).unwrap(); } } } diff --git a/sync/src/block_connector/test_write_dag_block_chain.rs b/sync/src/block_connector/test_write_dag_block_chain.rs index 21932ea913..20d3479214 100644 --- a/sync/src/block_connector/test_write_dag_block_chain.rs +++ b/sync/src/block_connector/test_write_dag_block_chain.rs @@ -36,9 +36,7 @@ pub fn gen_dag_blocks( time_service, ); last_block_hash = Some(block.id()); - let e = writeable_block_chain_service.try_connect( - block, - ); + let e = writeable_block_chain_service.try_connect(block); println!("try_connect result: {:?}", e); assert!(e.is_ok()); if (i + 1) % 3 == 0 { @@ -135,9 +133,7 @@ fn gen_fork_dag_block_chain( .unwrap(); parent_id = block.id(); - writeable_block_chain_service - .try_connect(block) - .unwrap(); + writeable_block_chain_service.try_connect(block).unwrap(); } return Some(parent_id); } diff --git a/sync/src/block_connector/write_block_chain.rs b/sync/src/block_connector/write_block_chain.rs index 697432b491..0095cde74c 100644 --- a/sync/src/block_connector/write_block_chain.rs +++ b/sync/src/block_connector/write_block_chain.rs @@ -16,17 +16,19 @@ use starcoin_consensus::dag::types::ghostdata::GhostdagData; use starcoin_consensus::{BlockDAG, FlexiDagStorage, FlexiDagStorageConfig}; use starcoin_crypto::HashValue; use starcoin_executor::VMMetrics; -use starcoin_flexidag::FlexidagService; use starcoin_flexidag::flexidag_service::{AddToDag, DumpTipsToAccumulator, UpdateDagTips}; +use starcoin_flexidag::FlexidagService; use starcoin_logger::prelude::*; use starcoin_service_registry::bus::{Bus, BusService}; use starcoin_service_registry::{ServiceContext, ServiceRef}; +use starcoin_storage::flexi_dag::KTotalDifficulty; use starcoin_storage::storage::CodecKVStore; use starcoin_storage::Store; use starcoin_time_service::{DagBlockTimeWindowService, TimeWindowResult}; use starcoin_txpool_api::TxPoolSyncService; use starcoin_types::block::BlockInfo; use starcoin_types::blockhash::BlockHashMap; +use starcoin_types::dag_block::KTotalDifficulty; use starcoin_types::header::DagHeader; use starcoin_types::{ block::{Block, BlockHeader, ExecutedBlock}, @@ -117,10 +119,11 @@ where .map(|metrics| metrics.chain_block_connect_time.start_timer()); let result = if block.header().parents_hash().is_some() { - assert!(transaction_parent.is_some(), "in dag branch, the transaction parent should not be none"); - self.connect_dag_inner( - block, - ) + assert!( + transaction_parent.is_some(), + "in dag branch, the transaction parent should not be none" + ); + self.connect_dag_inner(block) } else { self.connect_inner(block) }; @@ -245,10 +248,7 @@ where } #[cfg(test)] - pub fn apply_failed( - &mut self, - block: Block, - ) -> Result<()> { + pub fn apply_failed(&mut self, block: Block) -> Result<()> { use anyhow::bail; use starcoin_chain::verifier::FullVerifier; @@ -283,44 +283,26 @@ where let branch_total_difficulty = new_branch.get_total_difficulty()?; if branch_total_difficulty > main_total_difficulty { self.update_startup_info(new_branch.head_block().header())?; - - if self.dag.is_none() { - ctx.broadcast(NewHeadBlock(Arc::new(new_branch.head_block()), None)); - } else { - let dag_parents = self - .dag - .as_ref() - .expect("the dag should not be None") - .lock() - .expect("failed to lock the dag") - .get_parents(new_head_block)?; - ctx.broadcast(NewHeadBlock( - Arc::new(new_branch.head_block()), - Some(dag_parents), - )); - } - + ctx.broadcast(NewHeadBlock(Arc::new(new_branch.head_block()))); Ok(()) } else { bail!("no need to switch"); } } - pub fn select_head( - &mut self, - new_branch: BlockChain, - ) -> Result<()> { + pub fn select_head(&mut self, new_branch: BlockChain) -> Result<()> { let executed_block = new_branch.head_block(); let main_total_difficulty = self.main.get_total_difficulty()?; let branch_total_difficulty = new_branch.get_total_difficulty()?; let parent_is_main_head = self.is_main_head(&executed_block.header().parent_hash()); if branch_total_difficulty > main_total_difficulty { - let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = if !parent_is_main_head { + let (enacted_count, enacted_blocks, retracted_count, retracted_blocks) = + if !parent_is_main_head { self.find_ancestors_from_accumulator(&new_branch)? } else { (1, vec![executed_block.block.clone()], 0, vec![]) - }; + }; self.main = new_branch; self.do_new_head( @@ -438,7 +420,7 @@ where .storage .get_tips_by_block_id(executed_block.block.header().id()) .ok(); - self.broadcast_new_head(executed_block, dag_block_parents, next_tips); + self.broadcast_new_head(executed_block); Ok(()) } @@ -646,12 +628,7 @@ where Ok(blocks) } - fn broadcast_new_head( - &self, - block: ExecutedBlock, - dag_parents: Option>, - next_tips: Option>, - ) { + fn broadcast_new_head(&self, block: ExecutedBlock) { if let Some(metrics) = self.metrics.as_ref() { metrics .chain_select_head_total @@ -661,34 +638,25 @@ where if let Err(e) = self .bus - .broadcast(NewHeadBlock(Arc::new(block), dag_parents)) + .broadcast(NewHeadBlock(Arc::new(block))) { error!("Broadcast NewHeadBlock error: {:?}", e); } } - fn broadcast_new_branch( - &self, - block: ExecutedBlock, - ) { + fn broadcast_new_branch(&self, block: ExecutedBlock) { if let Some(metrics) = self.metrics.as_ref() { metrics .chain_select_head_total .with_label_values(&["new_branch"]) .inc() } - if let Err(e) = self - .bus - .broadcast(NewBranch(Arc::new(block))) - { + if let Err(e) = self.bus.broadcast(NewBranch(Arc::new(block))) { error!("Broadcast NewBranch error: {:?}", e); } } - fn switch_branch( - &mut self, - block: Block, - ) -> Result { + fn switch_branch(&mut self, block: Block) -> Result { let (block_info, fork) = self.find_or_fork( block.header(), dag_block_next_parent, @@ -727,7 +695,7 @@ where } (None, Some(mut branch)) => { // the block is not in the block, but the parent is - let result = branch.apply(block, None); + let result = branch.apply(block); let executed_block = result?; self.select_head(branch)?; Ok(ConnectOk::ExeConnectBranch(executed_block)) @@ -758,20 +726,14 @@ where self.switch_branch(block) } - fn apply_and_select_head( - &mut self, - block: Block, - ) -> Result { - let executed_block = self.main.apply(block, None)?; + fn apply_and_select_head(&mut self, block: Block) -> Result { + let executed_block = self.main.apply(block)?; let enacted_blocks = vec![executed_block.block().clone()]; self.do_new_head(executed_block.clone(), 1, enacted_blocks, 0, vec![])?; return Ok(ConnectOk::ExeConnectMain(executed_block)); } - fn add_to_dag( - &mut self, - header: &BlockHeader, - ) -> Result> { + fn add_to_dag(&mut self, header: &BlockHeader) -> Result> { let dag = self.dag.as_mut().expect("dag must be inited before using"); match dag .lock() @@ -787,13 +749,10 @@ where } } - fn connect_dag_inner( - &mut self, - block: Block, - ) -> Result { + fn connect_dag_inner(&mut self, block: Block) -> Result { let add_dag_result = async_std::task::block_on(self.flexidag_service.send(AddToDag { block_header: block.header().clone(), - }))??; + }))??; let selected_parent = self .storage .get_block_by_hash(add_dag_result.selected_parent)? @@ -802,7 +761,7 @@ where let mut transaction_parent = chain.status().head().id().clone(); for blue_hash in add_dag_result.mergeset_blues.mergeset_blues.iter() { if let Some(blue_block) = self.storage.get_block(blue_hash.to_owned())? { - match chain.apply(blue_block, Some(transaction_parent)) { + match chain.apply(blue_block) { Ok(executed_block) => transaction_parent = executed_block, Err(_) => warn!("failed to connect dag block: {:?}", e), } @@ -813,12 +772,22 @@ where } // select new head and update startup info(main but dag main) self.select_head_for_dag(chain)?; - Ok(ConnectOk::DagConnected) + Ok(ConnectOk::DagConnected(KTotalDifficulty { + head_block_id: self.main.status().head().id(), + total_difficulty: self.main.status().info().get_total_difficulty(), + })) } fn select_head_for_dag(&self, new_chain: BlockChain) -> Result<()> { - - + if new_chain.status().info.get_total_difficulty() + > self.main.status().info.get_total_difficulty() + { + let new_head_block = new_chain.head_block(); + self.update_startup_info(new_head_block.header())?; + self.main = new_chain; + self.broadcast_new_head(new_head_block); + } + Ok(()) } @@ -826,9 +795,13 @@ where if block_header.number() < self.storage.dag_fork_height(self.config.net().id().clone()) { Ok(()) } else { - self.flexidag_service.send( DumpTipsToAccumulator { + self.flexidag_service.send(DumpTipsToAccumulator { block_header, current_head_block_id: self.main.status().head().id().clone(), + k_total_difficulty: KTotalDifficulty { + head_block_id: self.main.status().info().id(), + total_difficulty: self.main.status().info().get_total_difficulty(), + }, }) } } @@ -838,6 +811,10 @@ where self.flexidag_service.send(UpdateDagTips { block_header, current_head_block_id: self.main.status().head().id().clone(), + k_total_difficulty: KTotalDifficulty { + head_block_id: self.main.status().head().id().clone(), + total_difficulty: self.main.status().info().get_total_difficulty(), + }, }) } else { Ok(()) // nothing to do @@ -860,7 +837,7 @@ where // let mut next_tips = Some(vec![]); let executed_block = self.connect_to_main(block)?.clone(); if let Some(block) = executed_block.block() { - self.broadcast_new_head(block.clone(), None, None); + self.broadcast_new_head(block.clone()); } return Ok(executed_block); } diff --git a/sync/src/sync.rs b/sync/src/sync.rs index 6d8622b1f0..b6bd70bbda 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -5,7 +5,7 @@ use crate::block_connector::BlockConnectorService; use crate::sync_metrics::SyncMetrics; use crate::tasks::{full_sync_task, sync_dag_full_task, AncestorEvent, SyncFetcher}; use crate::verified_rpc_client::{RpcVerifyError, VerifiedRpcClient}; -use anyhow::{format_err, Result, Ok}; +use anyhow::{format_err, Ok, Result}; use futures::executor::block_on; use futures::FutureExt; use futures_timer::Delay; @@ -17,8 +17,8 @@ use starcoin_chain_api::{ChainReader, ChainWriter}; use starcoin_config::NodeConfig; use starcoin_consensus::BlockDAG; use starcoin_executor::VMMetrics; -use starcoin_flexidag::{FlexidagService, flexidag_service}; use starcoin_flexidag::flexidag_service::GetDagAccumulatorInfo; +use starcoin_flexidag::{flexidag_service, FlexidagService}; use starcoin_logger::prelude::*; use starcoin_network::NetworkServiceRef; use starcoin_network::PeerEvent; @@ -100,23 +100,19 @@ impl SyncService { // let genesis = storage // .get_genesis()? // .ok_or_else(|| format_err!("Can not find genesis hash in storage."))?; - let dag_accumulator_info = - match storage.get_dag_accumulator_info()? { - Some(info) => Some(info), - None => { - warn!( - "Can not find dag accumulator info by head block id: {}, use genesis info.", - head_block_info.block_id(), - ); - None - } - }; + let dag_accumulator_info = match storage.get_dag_accumulator_info()? { + Some(info) => Some(info), + None => { + warn!( + "Can not find dag accumulator info by head block id: {}, use genesis info.", + head_block_info.block_id(), + ); + None + } + }; Ok(Self { sync_status: SyncStatus::new( - ChainStatus::new( - head_block.header.clone(), - head_block_info, - ), + ChainStatus::new(head_block.header.clone(), head_block_info), dag_accumulator_info, ), stage: SyncStage::NotStart, @@ -266,41 +262,43 @@ impl SyncService { network.clone(), )); - let op_local_dag_accumulator_info = self.flexidag_service.send(GetDagAccumulatorInfo).await??; + let op_local_dag_accumulator_info = + self.flexidag_service.send(GetDagAccumulatorInfo).await??; if let Some(local_dag_accumulator_info) = op_local_dag_accumulator_info { - let dag_sync_futs = rpc_client.get_dag_targets()?.into_iter().fold(Ok(vec![]), |mut futs, target_accumulator_infos| { - let (fut, task_handle, task_event_handle) = sync_dag_full_task( - local_dag_accumulator_info, - target_accumulator_info, - rpc_client.clone(), - dag_accumulator_store, - dag_accumulator_snapshot, - storage.clone(), - config.net().time_service(), - vm_metrics.clone(), - connector_service.clone(), - network.clone(), - skip_pow_verify, - dag.clone(), - block_chain_service.clone(), - config.net().id().clone(), - )?; - self_ref.notify(SyncBeginEvent { - target, - task_handle, - task_event_handle, - peer_selector, - })?; - if let Some(sync_task_total) = sync_task_total.as_ref() { - sync_task_total.with_label_values(&["start"]).inc(); - } - futs.and_then(|v| { - v.push(fut) - }) - })?.into_iter().fold(Ok(vec![]), |chain, fut| { - Ok(vec![fut.await?]) - })?; + let dag_sync_futs = rpc_client + .get_dag_targets()? + .into_iter() + .fold(Ok(vec![]), |mut futs, target_accumulator_infos| { + let (fut, task_handle, task_event_handle) = sync_dag_full_task( + local_dag_accumulator_info, + target_accumulator_info, + rpc_client.clone(), + dag_accumulator_store, + dag_accumulator_snapshot, + storage.clone(), + config.net().time_service(), + vm_metrics.clone(), + connector_service.clone(), + network.clone(), + skip_pow_verify, + dag.clone(), + block_chain_service.clone(), + config.net().id().clone(), + )?; + self_ref.notify(SyncBeginEvent { + target, + task_handle, + task_event_handle, + peer_selector, + })?; + if let Some(sync_task_total) = sync_task_total.as_ref() { + sync_task_total.with_label_values(&["start"]).inc(); + } + futs.and_then(|v| v.push(fut)) + })? + .into_iter() + .fold(Ok(vec![]), |chain, fut| Ok(vec![fut.await?]))?; assert!(dag_sync_futs.len() <= 1); if dag_sync_futs.len() == 1 { Ok(Some(dag_sync_futs[0])) diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index a1f95580a8..81469760d6 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -8,12 +8,14 @@ use futures::future::BoxFuture; use futures::FutureExt; use network_api::PeerId; use network_api::PeerProvider; +use starcoin_accumulator::accumulator_info::AccumulatorInfo; use starcoin_accumulator::{Accumulator, MerkleAccumulator}; use starcoin_chain::{verifier::BasicVerifier, BlockChain}; use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, ExecutedBlock}; -use starcoin_config::{G_CRATE_VERSION, Connect}; +use starcoin_config::{Connect, G_CRATE_VERSION}; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; +use starcoin_flexidag::flexidag_service::{AddToDag, GetDagTips, ForkDagAccumulator}; use starcoin_flexidag::FlexidagService; use starcoin_logger::prelude::*; use starcoin_service_registry::ServiceRef; @@ -32,10 +34,13 @@ pub struct SyncBlockData { pub(crate) block: Block, pub(crate) info: Option, pub(crate) peer_id: Option, - pub(crate) accumulator_root: Option, // the block belongs to this accumulator leaf - pub(crate) count_in_leaf: u64, // the number of the block in the accumulator leaf + pub(crate) accumulator_root: Option, // the block belongs to this dag accumulator leaf + pub(crate) count_in_leaf: u64, // the count of the block in the dag accumulator leaf + pub(crate) dag_accumulator_index: Option, // the index of the accumulator leaf which the block belogs to } + + impl SyncBlockData { pub fn new( block: Block, @@ -43,8 +48,7 @@ impl SyncBlockData { peer_id: Option, accumulator_root: Option, count_in_leaf: u64, - dag_block_headers: Option>, - dag_transaction_header: Option, + dag_acccumulator_index: Option, ) -> Self { Self { block, @@ -52,30 +56,15 @@ impl SyncBlockData { peer_id, accumulator_root, count_in_leaf, + dag_accumulator_index, } } } #[allow(clippy::from_over_into)] -impl - Into<( - Block, - Option, - Option, - )> for SyncBlockData -{ - fn into( - self, - ) -> ( - Block, - Option, - Option, - ) { - ( - self.block, - self.info, - self.peer_id, - ) +impl Into<(Block, Option, Option)> for SyncBlockData { + fn into(self) -> (Block, Option, Option) { + (self.block, self.info, self.peer_id) } } @@ -163,7 +152,7 @@ impl TaskState for BlockSyncTask { .fold(result_map, |mut result_map, (block, peer_id, _, _)| { result_map.insert( block.id(), - SyncBlockData::new(block, None, peer_id, None, 1, None, None), + SyncBlockData::new(block, None, peer_id, None, 1, None), ); result_map }) @@ -185,7 +174,7 @@ impl TaskState for BlockSyncTask { .await? .into_iter() .map(|(block, peer_id, _, _)| { - SyncBlockData::new(block, None, peer_id, None, 1, None, None) + SyncBlockData::new(block, None, peer_id, None, 1, None) }) .collect()) } @@ -221,7 +210,7 @@ impl TaskState for BlockSyncTask { pub struct BlockCollector { //node's current block info current_block_info: BlockInfo, - target: Option, + target: Option, // single chain use only // the block chain init by ancestor chain: BlockChain, event_handle: H, @@ -231,6 +220,7 @@ pub struct BlockCollector { dag_block_pool: Vec, target_accumulator_root: HashValue, flexidag_service: ServiceRef, + new_dag_accumulator_info: Option, } impl BlockCollector @@ -264,9 +254,14 @@ where dag_block_pool: Vec::new(), target_accumulator_root, flexidag_service, + new_dag_accumulator_info: None, } } + pub fn check_if_became_dag(&self) -> Result { + Ok(async_std::task::block_on(self.flexidag_service.send(GetDagTips))??.is_some()) + } + #[cfg(test)] pub fn apply_block_for_test( &mut self, @@ -283,7 +278,6 @@ where block_info: BlockInfo, action: BlockConnectAction, state: CollectorState, - dag_parents: Option>, ) -> Result { let total_difficulty = block_info.get_total_difficulty(); @@ -309,7 +303,6 @@ where // second, construct the block connect event. let block_connect_event = BlockConnectedEvent { block, - dag_parents, feedback: sender, action, }; @@ -342,11 +335,7 @@ where Ok(state) } - fn apply_block( - &mut self, - block: Block, - peer_id: Option, - ) -> Result<()> { + fn apply_block(&mut self, block: Block, peer_id: Option) -> Result<()> { if let Some((_failed_block, pre_peer_id, err, version)) = self .chain .get_storage() @@ -414,7 +403,8 @@ where fn broadcast_dag_chain_block( &mut self, - broadcast_blocks: Vec<(Block, BlockInfo, Option>, BlockConnectAction)>, + broadcast_blocks: Vec<(Block, BlockInfo, BlockConnectAction)>, + start_index: u64, ) -> Result { let state = if self.last_accumulator_root == self.target_accumulator_root { CollectorState::Enough @@ -422,29 +412,11 @@ where CollectorState::Need }; - let last_index = broadcast_blocks.len() - 1; - broadcast_blocks.into_iter().enumerate().for_each( - |(index, (block, block_info, dag_parents, action))| { - if last_index == index && state == CollectorState::Enough { - let _ = self.notify_connected_block( - block, - block_info, - action, - CollectorState::Enough, - dag_parents, - ); - } else { - let _ = self.notify_connected_block( - block, - block_info, - action, - CollectorState::Need, - dag_parents, - ); - } - }, - ); - + self.new_dag_accumulator_info = Some(async_std::task::block_on(self.flexidag_service.send(ForkDagAccumulator { + new_blocks: broadcast_blocks.into_iter().map(|(block, _, _)| block.id()).collect(), + dag_accumulator_index: start_index, + block_header_id: self.chain.head_block().id(), + }))??); return Ok(state); } @@ -481,20 +453,24 @@ where Ok(CollectorState::Need) }; - self.notify_connected_block(block, block_info, action, state?, None) + let result = self.notify_connected_block(block, block_info, action, state?); + match result { + Ok(state) => {} + Err(e) => { + error!("notify connected block error: {:?}", e); + Err(e) + } + } } - fn collect_dag_item( - &mut self, - item: SyncBlockData, - ) -> Result<()> { + fn collect_dag_item(&mut self, item: SyncBlockData) -> Result<()> { let (block, block_info, peer_id) = item.into(); let block_id = block.id(); let timestamp = block.header().timestamp(); let add_dag_result = async_std::task::block_on(self.flexidag_service.send(AddToDag { block_header: block.header().clone(), - }))??; + }))??; let selected_parent = self .storage .get_block_by_hash(add_dag_result.selected_parent)? @@ -528,85 +504,24 @@ where return match block_info { Some(block_info) => { - //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. + //If block_info exists, it means that this block was already executed and + // try connect in the previous sync, but the sync task was interrupted. //So, we just need to update chain and continue self.chain.connect(ExecutedBlock { block: block.clone(), block_info: block_info.clone(), })?; let block_info = self.chain.status().info; - Ok(( - block, - block_info, - BlockConnectAction::ConnectExecutedBlock, - )) + Ok((block, block_info, BlockConnectAction::ConnectExecutedBlock)) } None => { self.apply_block(block.clone(), peer_id)?; self.chain.time_service().adjust(timestamp); let block_info = self.chain.status().info; - Ok(( - block, - block_info, - BlockConnectAction::ConnectNewBlock, - )) + Ok((block, block_info, BlockConnectAction::ConnectNewBlock)) } }; } - - // fn process_received_block(&self, item: SyncBlockData, next_tips: &mut Option>) -> Result { - - // let (block, block_info, parent_hash, action) = self.collect_item(item, next_tips)?; - // ///////// - // // let (block, block_info, peer_id) = item.into(); - // // let timestamp = block.header().timestamp(); - // // let (block_info, action) = match block_info { - // // Some(block_info) => { - // // //If block_info exists, it means that this block was already executed and try connect in the previous sync, but the sync task was interrupted. - // // //So, we just need to update chain and continue - // // self.chain.connect(ExecutedBlock { - // // block: block.clone(), - // // block_info: block_info.clone(), - // // })?; - // // (block_info, BlockConnectAction::ConnectExecutedBlock) - // // } - // // None => { - // // self.apply_block(block.clone(), peer_id)?; - // // self.chain.time_service().adjust(timestamp); - // // ( - // // self.chain.status().info, - // // BlockConnectAction::ConnectNewBlock, - // // ) - // // } - // // }; - - // //verify target - // let state: Result = - // if block_info.block_accumulator_info.num_leaves - // == self.target.block_info.block_accumulator_info.num_leaves - // { - // if block_info != self.target.block_info { - // Err(TaskError::BreakError( - // RpcVerifyError::new_with_peers( - // self.target.peers.clone(), - // format!( - // "Verify target error, expect target: {:?}, collect target block_info:{:?}", - // self.target.block_info, - // block_info - // ), - // ) - // .into(), - // ) - // .into()) - // } else { - // Ok(CollectorState::Enough) - // } - // } else { - // Ok(CollectorState::Need) - // }; - - // self.notify_connected_block(block, block_info, action, state?, parent_hash) - // } } impl TaskResultCollector for BlockCollector @@ -627,14 +542,6 @@ where return Ok(CollectorState::Need); } else { process_block_pool = std::mem::take(&mut self.dag_block_pool); - - self.chain.status().tips_hash = Some( - process_block_pool - .iter() - .clone() - .map(|item| item.block.header().id()) - .collect(), - ); } } else { // it is a single chain @@ -664,11 +571,16 @@ where ); let (block, block_info, _, action) = block_to_broadcast.pop().unwrap(); // self.check_if_sync_complete(block_info) - self.broadcast_single_chain_block(block, block_info, action) - } - None => { - self.broadcast_dag_chain_block(block_to_broadcast) + match self.broadcast_single_chain_block(block, block_info, action) { + Ok(_) => { + if self.check_if_became_dag()? { + Ok(CollectorState::Enough) + } + } + Err(e) => Err(e), + } } + None => self.broadcast_dag_chain_block(block_to_broadcast, item.dag_accumulator_index), } } diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index cf92b552fb..36fea6074b 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -40,9 +40,12 @@ use stream_task::{ pub trait SyncFetcher: PeerOperator + BlockIdFetcher + BlockFetcher + BlockInfoFetcher { fn get_dag_targets(&self) -> Result> { - Ok(self.peer_selector().peer_infos().into_iter().map(|peer_info| { - peer_info.chain_info().dag_accumulator_info().clone() - }).collect()); + Ok(self + .peer_selector() + .peer_infos() + .into_iter() + .map(|peer_info| peer_info.chain_info().dag_accumulator_info().clone()) + .collect()); } fn get_best_target( @@ -304,14 +307,7 @@ pub trait BlockFetcher: Send + Sync { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture< - Result< - Vec<( - Block, - Option, - )>, - >, - >; + ) -> BoxFuture)>>>; } impl BlockFetcher for Arc @@ -321,15 +317,7 @@ where fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture< - '_, - Result< - Vec<( - Block, - Option, - )>, - >, - > { + ) -> BoxFuture<'_, Result)>>> { BlockFetcher::fetch_blocks(self.as_ref(), block_ids) } } @@ -338,15 +326,7 @@ impl BlockFetcher for VerifiedRpcClient { fn fetch_blocks( &self, block_ids: Vec, - ) -> BoxFuture< - '_, - Result< - Vec<( - Block, - Option, - )>, - >, - > { + ) -> BoxFuture<'_, Result)>>> { self.get_blocks(block_ids.clone()) .and_then(|blocks| async move { let results = block_ids @@ -422,7 +402,7 @@ impl BlockLocalStore for Arc { let block_info = self.get_block_info(id)?; Ok(Some(SyncBlockData::new( - block, block_info, None, None, 1, None, None, + block, block_info, None, None, 1, None, ))) } None => Ok(None), @@ -440,7 +420,6 @@ pub enum BlockConnectAction { #[derive(Clone, Debug)] pub struct BlockConnectedEvent { pub block: Block, - pub dag_parents: Option>, pub feedback: Option>, pub action: BlockConnectAction, } diff --git a/sync/src/tasks/sync_dag_block_task.rs b/sync/src/tasks/sync_dag_block_task.rs index 4e31565e0f..2428898926 100644 --- a/sync/src/tasks/sync_dag_block_task.rs +++ b/sync/src/tasks/sync_dag_block_task.rs @@ -66,8 +66,6 @@ impl SyncDagBlockTask { block_id: block_id.clone(), block: None, peer_id: None, - dag_parents: vec![], - dag_transaction_header: None, }); }); @@ -76,15 +74,7 @@ impl SyncDagBlockTask { .fetch_blocks(absent_block) .await? .iter() - .map(|(block, peer_info)| { - ( - block.header().id(), - ( - block.clone(), - peer_info.clone(), - ), - ) - }) + .map(|(block, peer_info)| (block.header().id(), (block.clone(), peer_info.clone()))) .collect::>(); // should return the block in order @@ -116,6 +106,7 @@ impl SyncDagBlockTask { peer_id: item.peer_id, accumulator_root: Some(snapshot.accumulator_info.get_accumulator_root().clone()), count_in_leaf: snapshot.child_hashes.len() as u64, + dag_accumulator_index: Some(index), }) .collect()) } diff --git a/sync/src/tasks/tests.rs b/sync/src/tasks/tests.rs index e0382bad68..88895a69c4 100644 --- a/sync/src/tasks/tests.rs +++ b/sync/src/tasks/tests.rs @@ -766,7 +766,7 @@ impl MockLocalBlockStore { ); self.store.lock().unwrap().insert( block.id(), - SyncBlockData::new(block.clone(), Some(block_info), None, None, 1, None, None), + SyncBlockData::new(block.clone(), Some(block_info), None, None, 1, None), ); } } diff --git a/sync/src/verified_rpc_client.rs b/sync/src/verified_rpc_client.rs index 7272f12e7b..265fdba26c 100644 --- a/sync/src/verified_rpc_client.rs +++ b/sync/src/verified_rpc_client.rs @@ -381,14 +381,7 @@ impl VerifiedRpcClient { pub async fn get_blocks( &self, ids: Vec, - ) -> Result< - Vec< - Option<( - Block, - Option, - )>, - >, - > { + ) -> Result)>>> { let peer_id = self.select_a_peer()?; let start_time = Instant::now(); let blocks = self.client.get_blocks(peer_id.clone(), ids.clone()).await?; diff --git a/test-helper/src/network.rs b/test-helper/src/network.rs index 42320fb360..c295f01dd8 100644 --- a/test-helper/src/network.rs +++ b/test-helper/src/network.rs @@ -196,7 +196,7 @@ impl ServiceFactory for MockNetworkServiceFactory { let dag_tips = storage.get_tips_by_block_id(head_block_hash)?; let chain_status = ChainStatus::new(head_block_header.clone(), head_block_info, Some(dag_tips)); - let dag_accumulator_info = storage.get_dag_accumulator_info(head_block_hash)?; + let dag_accumulator_info = storage.get_dag_accumulator_info()?; let chain_state_info = ChainInfo::new( config.net().chain_id(), genesis_hash, diff --git a/types/src/block.rs b/types/src/block.rs index 53301e93c9..a8cbb0bf33 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -51,8 +51,8 @@ impl std::fmt::Display for BlockHeaderExtra { impl<'de> Deserialize<'de> for BlockHeaderExtra { fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { if deserializer.is_human_readable() { let s = ::deserialize(deserializer)?; @@ -79,8 +79,8 @@ impl<'de> Deserialize<'de> for BlockHeaderExtra { impl Serialize for BlockHeaderExtra { fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, + where + S: Serializer, { if serializer.is_human_readable() { format!("0x{}", hex::encode(self.0)).serialize(serializer) @@ -91,7 +91,7 @@ impl Serialize for BlockHeaderExtra { } #[derive( -Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, JsonSchema, + Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, JsonSchema, )] pub struct BlockIdAndNumber { pub id: HashValue, @@ -159,7 +159,6 @@ pub struct BlockHeader { /// Parents hash. #[serde(skip_serializing_if = "Option::is_none")] parents_hash: ParentsHash, - } impl BlockHeader { @@ -371,8 +370,8 @@ impl BlockHeader { impl<'de> Deserialize<'de> for BlockHeader { fn deserialize(deserializer: D) -> Result>::Error> - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(rename = "BlockHeader")] @@ -391,7 +390,7 @@ impl<'de> Deserialize<'de> for BlockHeader { chain_id: ChainId, nonce: u32, extra: BlockHeaderExtra, - parents_hash:ParentsHash, + parents_hash: ParentsHash, } let header_data = BlockHeaderData::deserialize(deserializer)?; @@ -410,7 +409,7 @@ impl<'de> Deserialize<'de> for BlockHeader { header_data.chain_id, header_data.nonce, header_data.extra, - header_data.parents_hash + header_data.parents_hash, ); Ok(block_header) } @@ -507,7 +506,7 @@ pub struct RawBlockHeader { /// The chain id pub chain_id: ChainId, /// parents hash - pub parents_hash:ParentsHash, + pub parents_hash: ParentsHash, } #[derive(Default)] @@ -610,7 +609,7 @@ impl BlockHeaderBuilder { } #[derive( -Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, + Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, )] pub struct BlockBody { /// The transactions in this block. @@ -680,8 +679,8 @@ pub struct Block { impl Block { pub fn new(header: BlockHeader, body: B) -> Self - where - B: Into, + where + B: Into, { Block { header, @@ -797,7 +796,7 @@ impl Sample for Block { /// `BlockInfo` is the object we store in the storage. It consists of the /// block as well as the execution result of this block. #[derive( -Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, JsonSchema, + Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, CryptoHasher, CryptoHash, JsonSchema, )] pub struct BlockInfo { /// Block id @@ -1014,7 +1013,7 @@ impl BlockTemplate { body_hash: self.body_hash, difficulty: self.difficulty, chain_id: self.chain_id, - parents_hash: self.parents_hash.clone() + parents_hash: self.parents_hash.clone(), } } @@ -1061,7 +1060,7 @@ impl BlockTemplate { self.chain_id, nonce, extra, - self.parents_hash + self.parents_hash, ) } } @@ -1074,10 +1073,7 @@ pub struct ExecutedBlock { impl ExecutedBlock { pub fn new(block: Block, block_info: BlockInfo) -> Self { - ExecutedBlock { - block, - block_info, - } + ExecutedBlock { block, block_info } } pub fn total_difficulty(&self) -> U256 { diff --git a/types/src/dag_block.rs b/types/src/dag_block.rs index bc089a92e5..672b728850 100644 --- a/types/src/dag_block.rs +++ b/types/src/dag_block.rs @@ -945,3 +945,20 @@ impl EpochUncleSummary { } } } +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct KTotalDifficulty { + pub head_block_id: HashValue, + pub total_difficulty: U256, +} + +impl Ord for KTotalDifficulty { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.total_difficulty.cmp(&other.total_difficulty) + } +} + +impl PartialOrd for KTotalDifficulty { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/types/src/header.rs b/types/src/header.rs index 644f1bb064..a93ddcde36 100644 --- a/types/src/header.rs +++ b/types/src/header.rs @@ -21,7 +21,9 @@ pub struct DagHeader { impl DagHeader { pub fn new(block_header: BlockHeader) -> Self { Self { - parents_hash: block_header.parents_hash().expect("dag block must have parents hash"), + parents_hash: block_header + .parents_hash() + .expect("dag block must have parents hash"), block_header, } } diff --git a/types/src/startup_info.rs b/types/src/startup_info.rs index fc731a580f..4e08226411 100644 --- a/types/src/startup_info.rs +++ b/types/src/startup_info.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::block::{BlockHeader, BlockInfo, BlockNumber}; +use crate::dag_block::KTotalDifficulty; use anyhow::Result; use bcs_ext::{BCSCodec, Sample}; use schemars::JsonSchema; @@ -11,6 +12,7 @@ use starcoin_accumulator::MerkleAccumulator; use starcoin_crypto::HashValue; use starcoin_uint::U256; use starcoin_vm_types::genesis_config::ChainId; +use std::collections::BTreeSet; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::Formatter; @@ -23,6 +25,7 @@ pub struct ChainInfo { genesis_hash: HashValue, status: ChainStatus, flexi_dag_accumulator_info: Option, + k_total_difficulties: Option>, } impl ChainInfo { @@ -31,12 +34,14 @@ impl ChainInfo { genesis_hash: HashValue, status: ChainStatus, flexi_dag_accumulator_info: Option, + k_total_difficulties: Option>, ) -> Self { Self { chain_id, genesis_hash, status, flexi_dag_accumulator_info, + k_total_difficulties, } } @@ -75,6 +80,10 @@ impl ChainInfo { self.status.info.get_total_difficulty() } + pub fn k_total_difficulties(&self) -> &Option> { + &self.k_total_difficulties + } + pub fn into_inner(self) -> (ChainId, HashValue, ChainStatus) { (self.chain_id, self.genesis_hash, self.status) } @@ -90,6 +99,7 @@ impl ChainInfo { rand::random::(), rand::random::(), )), + k_total_difficulties: Some(BTreeSet::new()), } } } @@ -101,6 +111,7 @@ impl std::default::Default for ChainInfo { genesis_hash: HashValue::default(), status: ChainStatus::sample(), flexi_dag_accumulator_info: Some(AccumulatorInfo::default()), + k_total_difficulties: Some(BTreeSet::new()), } } } @@ -126,10 +137,7 @@ pub struct ChainStatus { impl ChainStatus { pub fn new(head: BlockHeader, info: BlockInfo) -> Self { - Self { - head, - info, - } + Self { head, info } } pub fn random() -> Self { @@ -219,7 +227,7 @@ pub struct StartupInfo { pub main: HashValue, /// dag accumulator info hash - pub dag_main: Option + pub dag_main: Option, } impl fmt::Display for StartupInfo { @@ -233,17 +241,14 @@ impl fmt::Display for StartupInfo { impl StartupInfo { pub fn new(main: HashValue) -> Self { - Self { + Self { main, dag_main: None, - } + } } pub fn new_with_dag(main: HashValue, dag_main: Option) -> Self { - Self { - main, - dag_main, - } + Self { main, dag_main } } pub fn update_main(&mut self, new_head: HashValue) { @@ -261,8 +266,6 @@ impl StartupInfo { pub fn get_dag_main(&self) -> Option { self.dag_main } - - } impl TryFrom> for StartupInfo { diff --git a/types/src/system_events.rs b/types/src/system_events.rs index 6221778ab4..0fdc0c8899 100644 --- a/types/src/system_events.rs +++ b/types/src/system_events.rs @@ -10,7 +10,7 @@ use starcoin_crypto::HashValue; use starcoin_vm_types::genesis_config::ConsensusStrategy; use std::sync::Arc; #[derive(Clone, Debug)] -pub struct NewHeadBlock(pub Arc, pub Option>); +pub struct NewHeadBlock(pub Arc); /// may be uncle block #[derive(Clone, Debug)] From edb1dd4ef17126778631cf6e1b60c9155d3cd3a9 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Fri, 10 Nov 2023 17:19:33 +0800 Subject: [PATCH 16/17] add k total difficulty in chain info --- chain/src/chain.rs | 9 +++++---- cmd/peer-watcher/src/lib.rs | 1 + network/api/src/tests.rs | 4 ++++ network/tests/network_service_test.rs | 1 + rpc/server/src/module/chain_rpc.rs | 2 +- storage/src/lib.rs | 17 +++++++++++++---- sync/src/sync.rs | 4 ++-- sync/src/tasks/mod.rs | 18 ++++++++++++++++-- test-helper/src/network.rs | 7 +++++-- types/src/block.rs | 4 +--- .../src/fork_chain.rs | 3 ++- 11 files changed, 51 insertions(+), 19 deletions(-) diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 51f8147297..03944e3363 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -586,14 +586,15 @@ impl BlockChain { impl ChainReader for BlockChain { fn info(&self) -> ChainInfo { + let (dag_accumulator, k_total_difficulties) = self.storage.get_lastest_snapshot()?.map(|snapshot| { + (Some(snapshot.accumulator_info), Some(snapshot.k_total_difficulties)) + }).unwrap_or((None, None)); ChainInfo::new( self.status.head.header().chain_id(), self.genesis_hash, self.status.status.clone(), - self.storage.get_dag_accumulator_info().expect(&format!( - "the dag accumulator info cannot be found by id: {}", - self.status.head.header().id() - )), + dag_accumulator, + k_total_difficulties, ) } diff --git a/cmd/peer-watcher/src/lib.rs b/cmd/peer-watcher/src/lib.rs index 8b9d2d2500..bb75a86819 100644 --- a/cmd/peer-watcher/src/lib.rs +++ b/cmd/peer-watcher/src/lib.rs @@ -24,6 +24,7 @@ pub fn build_lighting_network( chain_info.genesis_hash(), chain_info.status().clone(), chain_info.dag_accumulator_info().clone(), + chain_info.k_total_difficulties().clone(), ); build_network_worker( network_config, diff --git a/network/api/src/tests.rs b/network/api/src/tests.rs index cf18f2ce9b..5264d236b9 100644 --- a/network/api/src/tests.rs +++ b/network/api/src/tests.rs @@ -41,6 +41,7 @@ fn test_peer_selector() { HashValue::zero(), mock_chain_status(100.into()), None, + None, ), vec![], vec![], @@ -53,6 +54,7 @@ fn test_peer_selector() { HashValue::zero(), mock_chain_status(99.into()), None, + None, ), vec![], vec![], @@ -65,6 +67,7 @@ fn test_peer_selector() { HashValue::zero(), mock_chain_status(100.into()), None, + None, ), vec![], vec![], @@ -77,6 +80,7 @@ fn test_peer_selector() { HashValue::zero(), mock_chain_status(1.into()), None, + None, ), vec![], vec![], diff --git a/network/tests/network_service_test.rs b/network/tests/network_service_test.rs index 15b6b9318f..897a0c35c9 100644 --- a/network/tests/network_service_test.rs +++ b/network/tests/network_service_test.rs @@ -39,6 +39,7 @@ fn build_test_network_services(num: usize) -> Vec { HashValue::random(), ChainStatus::random(), None, + None, ); for _index in 0..num { let mut boot_nodes = Vec::new(); diff --git a/rpc/server/src/module/chain_rpc.rs b/rpc/server/src/module/chain_rpc.rs index eea1652095..db62630e2e 100644 --- a/rpc/server/src/module/chain_rpc.rs +++ b/rpc/server/src/module/chain_rpc.rs @@ -73,7 +73,7 @@ where let fut = async move { let chain_status = service.main_status().await?; //TODO get chain info from chain service. - Ok(ChainInfo::new(chain_id, genesis_hash, chain_status, None).into()) + Ok(ChainInfo::new(chain_id, genesis_hash, chain_status, None, None).into()) }; Box::pin(fut.boxed().map_err(map_err)) } diff --git a/storage/src/lib.rs b/storage/src/lib.rs index a3546ee7ec..3741e78ecb 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -15,7 +15,7 @@ use crate::{ transaction::TransactionStorage, transaction_info::{TransactionInfoHashStorage, TransactionInfoStorage}, }; -use anyhow::{bail, format_err, Error, Ok, Result}; +use anyhow::{anyhow, bail, format_err, Error, Ok, Result}; use flexi_dag::{SyncFlexiDagSnapshot, SyncFlexiDagSnapshotStorage, SyncFlexiDagStorage}; use network_p2p_types::peer_id::PeerId; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -23,7 +23,7 @@ use once_cell::sync::Lazy; use starcoin_accumulator::{ accumulator_info::{self, AccumulatorInfo}, node::AccumulatorStoreType, - AccumulatorTreeStore, + AccumulatorTreeStore, MerkleAccumulator, Accumulator, }; use starcoin_config::ChainNetworkID; use starcoin_crypto::HashValue; @@ -339,6 +339,7 @@ pub trait SyncFlexiDagStore { fn get_dag_accumulator_info(&self) -> Result>; fn get_tips_by_block_id(&self, block_id: HashValue) -> Result>; fn dag_fork_height(&self, id: ChainNetworkID) -> BlockNumber; + fn get_lastest_snapshot(&self) -> Result>; } // TODO: remove Arc, we can clone Storage directly. @@ -456,12 +457,13 @@ impl BlockStore for Storage { let head_block_info = self.get_block_info(head_block.id())?.ok_or_else(|| { format_err!("Startup block info {:?} should exist", startup_info.main) })?; - + let snapshot = self.get_lastest_snapshot()?.ok_or_else(error || anyhow!("latest snapshot is none"))?; let chain_info = ChainInfo::new( head_block.chain_id(), genesis_hash, ChainStatus::new(head_block.clone(), head_block_info), - self.get_dag_accumulator_info()?, + Some(snapshot.accumulator_info), + Some(snapshot.k_total_difficulties), ); Ok(Some(chain_info)) } @@ -674,6 +676,13 @@ impl SyncFlexiDagStore for Storage { self.flexi_dag_storage.get_snapshot_storage() } + fn get_lastest_snapshot(&self) -> Result> { + let info = self.get_dag_accumulator_info()?.ok_or_else(error || anyhow!("dag startup info is none"))?; + let merkle_tree = MerkleAccumulator::new_with_info(info, storage.get_accumulator_store(AccumulatorStoreType::SyncDag)); + let key = merkle_tree.get_leaf(merkle_tree.num_leaves() - 1)?.ok_or_else(errors || anyhow!("faile to get the key since it is none"))?; + self.query_by_hash(key) + } + fn get_dag_accumulator_info(&self) -> Result> { let startup_info = self.get_startup_info()?; if startup_info.is_none() { diff --git a/sync/src/sync.rs b/sync/src/sync.rs index b6bd70bbda..367896773a 100644 --- a/sync/src/sync.rs +++ b/sync/src/sync.rs @@ -267,7 +267,7 @@ impl SyncService { if let Some(local_dag_accumulator_info) = op_local_dag_accumulator_info { let dag_sync_futs = rpc_client - .get_dag_targets()? + .get_dag_targets(current_block_info.get_total_difficulty(), local_dag_accumulator_info.get_num_leaves())? .into_iter() .fold(Ok(vec![]), |mut futs, target_accumulator_infos| { let (fut, task_handle, task_event_handle) = sync_dag_full_task( @@ -681,7 +681,7 @@ impl EventHandler for SyncService { impl EventHandler for SyncService { fn handle_event(&mut self, msg: NewHeadBlock, ctx: &mut ServiceContext) { - let NewHeadBlock(block, tips_hash) = msg; + let NewHeadBlock(block) = msg; if self.sync_status.update_chain_status(ChainStatus::new( block.header().clone(), block.block_info.clone(), diff --git a/sync/src/tasks/mod.rs b/sync/src/tasks/mod.rs index 36fea6074b..678722f2f6 100644 --- a/sync/src/tasks/mod.rs +++ b/sync/src/tasks/mod.rs @@ -39,12 +39,26 @@ use stream_task::{ }; pub trait SyncFetcher: PeerOperator + BlockIdFetcher + BlockFetcher + BlockInfoFetcher { - fn get_dag_targets(&self) -> Result> { + fn get_dag_targets(&self, total_difficulty: U256, local_dag_accumulator_leaf_num: u64) -> Result> { Ok(self .peer_selector() .peer_infos() .into_iter() - .map(|peer_info| peer_info.chain_info().dag_accumulator_info().clone()) + .filter(|peer_info| { + match (peer_info.chain_info().dag_accumulator_info(), peer_info.chain_info().k_total_difficulties()) { + (Some(info), Some(k)) => { + k.first() <= total_difficulty || info.get_num_leaves() > local_dag_accumulator_leaf_num + } + (NOne, None) => false, + _ => { + warn!("dag accumulator is inconsistent with k total difficulty"); + false + } + } + }) + .map(|peer_info| { + peer_info.chain_info().dag_accumulator_info().clone() + }) .collect()); } diff --git a/test-helper/src/network.rs b/test-helper/src/network.rs index c295f01dd8..d8b5c0cf88 100644 --- a/test-helper/src/network.rs +++ b/test-helper/src/network.rs @@ -195,13 +195,16 @@ impl ServiceFactory for MockNetworkServiceFactory { .ok_or_else(|| format_err!("can't get block info by hash {}", head_block_hash))?; let dag_tips = storage.get_tips_by_block_id(head_block_hash)?; let chain_status = - ChainStatus::new(head_block_header.clone(), head_block_info, Some(dag_tips)); - let dag_accumulator_info = storage.get_dag_accumulator_info()?; + ChainStatus::new(head_block_header.clone(), head_block_info); + let (dag_accumulator_info, k_total_difficulties) = storage.get_lastest_snapshot()?.map(|snapshot| { + (Some(snapshot.accumulator_info), Some(snapshot.k_total_difficulties)) + }).unwrap_or((None, None)); let chain_state_info = ChainInfo::new( config.net().chain_id(), genesis_hash, chain_status.clone(), dag_accumulator_info.clone(), + k_total_difficulties, ); let actor_service = NetworkActorService::new(config, chain_state_info, rpc, peer_message_handle.clone())?; diff --git a/types/src/block.rs b/types/src/block.rs index a8cbb0bf33..1b091ab305 100644 --- a/types/src/block.rs +++ b/types/src/block.rs @@ -845,9 +845,7 @@ impl BlockInfo { &self.block_id } - pub fn transaction_parent(&self) -> Option { - self.transaction_parent.clone() - } + } impl Sample for BlockInfo { diff --git a/vm/starcoin-transactional-test-harness/src/fork_chain.rs b/vm/starcoin-transactional-test-harness/src/fork_chain.rs index 2ad3fb7311..b5cf984a64 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_chain.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_chain.rs @@ -132,7 +132,7 @@ impl ForkBlockChain { self.number_hash_map .insert(self.current_number, block.header().id()); self.status = Some(ChainStatusWithBlock { - status: ChainStatus::new(block.header().clone(), block_info.clone(), None), + status: ChainStatus::new(block.header().clone(), block_info.clone()), head: block.clone(), }); self.storage.save_block_info(block_info)?; @@ -199,6 +199,7 @@ impl ChainApi for MockChainApi { HashValue::random(), status.status, None, + None, ))), None => match client { Some(client) => client.info().await.map_err(|e| anyhow!("{}", e)), From 8801176de2e1f38a473082b1e5fccf7d5be57cc4 Mon Sep 17 00:00:00 2001 From: jackzhhuang Date: Fri, 10 Nov 2023 18:02:53 +0800 Subject: [PATCH 17/17] add sync finish logic --- flexidag/src/flexidag_service.rs | 39 +++++++++++++++++++++++++++---- sync/src/tasks/block_sync_task.rs | 7 +++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/flexidag/src/flexidag_service.rs b/flexidag/src/flexidag_service.rs index 8966d0340e..a83edf2de1 100644 --- a/flexidag/src/flexidag_service.rs +++ b/flexidag/src/flexidag_service.rs @@ -4,7 +4,7 @@ use std::{ }; use anyhow::{anyhow, bail, Error, Ok, Result}; -use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator}; +use starcoin_accumulator::{accumulator_info::AccumulatorInfo, Accumulator, MerkleAccumulator, node::AccumulatorStoreType}; use starcoin_config::{NodeConfig, TimeService}; use starcoin_consensus::{dag::types::ghostdata::GhostdagData, BlockDAG}; use starcoin_crypto::HashValue; @@ -14,7 +14,7 @@ use starcoin_service_registry::{ use starcoin_storage::{ flexi_dag::{KTotalDifficulty, SyncFlexiDagSnapshot, SyncFlexiDagSnapshotHasher}, storage::CodecKVStore, - BlockStore, Storage, SyncFlexiDagStore, block_info::BlockInfoStore, + BlockStore, Storage, SyncFlexiDagStore, block_info::BlockInfoStore, Store, }; use starcoin_types::{block::BlockHeader, header::DagHeader, startup_info}; @@ -127,6 +127,15 @@ impl ServiceRequest for ForkDagAccumulator { type Response = anyhow::Result; } +#[derive(Debug, Clone)] +pub struct FinishSync { + pub dag_accumulator_info: AccumulatorInfo, +} + +impl ServiceRequest for FinishSync { + type Response = anyhow::Result<()>; +} + pub struct TipInfo { tips: Option>, // some is for dag or the state of the chain is still in old version k_total_difficulties: BTreeSet, @@ -536,8 +545,28 @@ impl ServiceHandler for FlexidagService { } else { self.merge_from_small_dag(msg) } - - - // append the ForkDagAccumulator.new_blocks and the fetched blocks above into the forked dag accumulator } } + +impl ServiceHandler for FlexidagService { + fn handle( + &mut self, + msg: FinishSync, + _ctx: &mut ServiceContext, + ) -> Result<()> { + let dag_accumulator = self.dag_accumulator.ok_or_else(|| anyhow!("the dag_accumulator is none when sync finish"))?; + let local_info = dag_accumulator.get_info(); + if msg.dag_accumulator_info.get_num_leaves() < local_info.get_num_leaves() { + let mut new_dag_accumulator = MerkleAccumulator::new_with_info(msg.dag_accumulator_info, self.storage.get_accumulator_store(AccumulatorStoreType::SyncDag)); + for index in msg.dag_accumulator_info.get_num_leaves()..local_info.get_num_leaves() { + let key = dag_accumulator.get_leaf(index)?.ok_or_else(|| anyhow!("the dag_accumulator leaf is none when sync finish"))?; + new_dag_accumulator.append(&[key])?; + } + self.dag_accumulator = Some(new_dag_accumulator); + Ok(()) + } else { + self.dag_accumulator = Some(MerkleAccumulator::new_with_info(msg.dag_accumulator_info, self.storage.get_accumulator_store(AccumulatorStoreType::SyncDag))); + Ok(()) + } + } +} \ No newline at end of file diff --git a/sync/src/tasks/block_sync_task.rs b/sync/src/tasks/block_sync_task.rs index 81469760d6..7f0e0cdaba 100644 --- a/sync/src/tasks/block_sync_task.rs +++ b/sync/src/tasks/block_sync_task.rs @@ -15,7 +15,7 @@ use starcoin_chain_api::{ChainReader, ChainWriter, ConnectBlockError, ExecutedBl use starcoin_config::{Connect, G_CRATE_VERSION}; use starcoin_consensus::BlockDAG; use starcoin_crypto::HashValue; -use starcoin_flexidag::flexidag_service::{AddToDag, GetDagTips, ForkDagAccumulator}; +use starcoin_flexidag::flexidag_service::{AddToDag, GetDagTips, ForkDagAccumulator, FinishSync}; use starcoin_flexidag::FlexidagService; use starcoin_logger::prelude::*; use starcoin_service_registry::ServiceRef; @@ -417,6 +417,11 @@ where dag_accumulator_index: start_index, block_header_id: self.chain.head_block().id(), }))??); + if state == State::Enough { + async_std::task::block_on(self.flexidag_service.send(FinishSync { + dag_accumulator_info: self.new_dag_accumulator_info.clone(), + }))?? + } return Ok(state); }