diff --git a/applications/tari_collectibles/web-app/package-lock.json b/applications/tari_collectibles/web-app/package-lock.json
index f67cdad75c5..a79f8ef0ac0 100644
--- a/applications/tari_collectibles/web-app/package-lock.json
+++ b/applications/tari_collectibles/web-app/package-lock.json
@@ -12,7 +12,7 @@
"@emotion/styled": "^11.3.0",
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
- "@tauri-apps/api": "^1.0.0-beta.8",
+ "@tauri-apps/api": "^1.0.0-rc.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
@@ -3501,9 +3501,12 @@
}
},
"node_modules/@tauri-apps/api": {
- "version": "1.0.0-beta.8",
- "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.0-beta.8.tgz",
- "integrity": "sha512-a56lXB7XvQ4+fKtT0pxpkjTSKhyrQ1Vmjyvt2ox3mT9xw3l7s8IOKHJ1WuqW6TA6xdoy3Cyja3Z3prw8hflS7g==",
+ "version": "1.0.0-rc.1",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.0-rc.1.tgz",
+ "integrity": "sha512-VBUOmfT8ea02JB/Qr+FHeaLnug5BRA7ro2pX47q0GZCbZsU9b+iPnOXl0ShJwT0melR7B6iamyhDwkHStMVfQA==",
+ "dependencies": {
+ "type-fest": "2.11.2"
+ },
"engines": {
"node": ">= 12.13.0",
"npm": ">= 6.6.0",
@@ -3514,6 +3517,17 @@
"url": "https://opencollective.com/tauri"
}
},
+ "node_modules/@tauri-apps/api/node_modules/type-fest": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.11.2.tgz",
+ "integrity": "sha512-reW2Y2Mpn0QNA/5fvtm5doROLwDPu2zOm5RtY7xQQS05Q7xgC8MOZ3yPNaP9m/s/sNjjFQtHo7VCNqYW2iI+Ig==",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "7.31.2",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
@@ -24302,9 +24316,19 @@
}
},
"@tauri-apps/api": {
- "version": "1.0.0-beta.8",
- "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.0-beta.8.tgz",
- "integrity": "sha512-a56lXB7XvQ4+fKtT0pxpkjTSKhyrQ1Vmjyvt2ox3mT9xw3l7s8IOKHJ1WuqW6TA6xdoy3Cyja3Z3prw8hflS7g=="
+ "version": "1.0.0-rc.1",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.0-rc.1.tgz",
+ "integrity": "sha512-VBUOmfT8ea02JB/Qr+FHeaLnug5BRA7ro2pX47q0GZCbZsU9b+iPnOXl0ShJwT0melR7B6iamyhDwkHStMVfQA==",
+ "requires": {
+ "type-fest": "2.11.2"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.11.2.tgz",
+ "integrity": "sha512-reW2Y2Mpn0QNA/5fvtm5doROLwDPu2zOm5RtY7xQQS05Q7xgC8MOZ3yPNaP9m/s/sNjjFQtHo7VCNqYW2iI+Ig=="
+ }
+ }
},
"@testing-library/dom": {
"version": "7.31.2",
diff --git a/applications/tari_collectibles/web-app/package.json b/applications/tari_collectibles/web-app/package.json
index a75aea0d100..401335bf5d5 100644
--- a/applications/tari_collectibles/web-app/package.json
+++ b/applications/tari_collectibles/web-app/package.json
@@ -7,7 +7,7 @@
"@emotion/styled": "^11.3.0",
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
- "@tauri-apps/api": "^1.0.0-beta.8",
+ "@tauri-apps/api": "^1.0.0-rc.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
diff --git a/applications/tari_collectibles/web-app/src/App.js b/applications/tari_collectibles/web-app/src/App.js
index e10288d4065..20a74022a33 100644
--- a/applications/tari_collectibles/web-app/src/App.js
+++ b/applications/tari_collectibles/web-app/src/App.js
@@ -122,9 +122,23 @@ const AccountsMenu = (props) => {
const [accounts, setAccounts] = useState([]);
const [error, setError] = useState("");
- useEffect(() => {
- async function inner() {
- console.log("refreshing accounts");
+ useEffect(async () => {
+ console.log("refreshing accounts");
+ setError("");
+ await binding
+ .command_asset_wallets_list()
+ .then((accounts) => {
+ console.log("accounts", accounts);
+ setAccounts(accounts);
+ })
+ .catch((e) => {
+ // todo error handling
+ console.error("accounts_list error:", e);
+ setError(e.message);
+ });
+
+ await listen("asset_wallets::updated", (event) => {
+ console.log("accounts have changed");
setError("");
binding
.command_asset_wallets_list()
@@ -137,24 +151,7 @@ const AccountsMenu = (props) => {
console.error("accounts_list error:", e);
setError(e.message);
});
-
- await listen("asset_wallets::updated", (event) => {
- console.log("accounts have changed");
- setError("");
- binding
- .command_asset_wallets_list()
- .then((accounts) => {
- console.log("accounts", accounts);
- setAccounts(accounts);
- })
- .catch((e) => {
- // todo error handling
- console.error("accounts_list error:", e);
- setError(e.message);
- });
- });
- }
- inner();
+ });
}, [props.walletId]);
// todo: hide accounts when not authenticated
@@ -211,7 +208,8 @@ ProtectedRoute.propTypes = {
};
function App() {
- const [loading, setLoading] = useState(true);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
const [authenticated, setAuthenticated] = useState(false);
const [walletId, setWalletId] = useState("");
const setPassword = useState("")[1];
@@ -223,9 +221,13 @@ function App() {
binding
.command_create_db()
.then((r) => setLoading(false))
- .catch((e) => console.error(e));
+ .catch((e) => {
+ setLoading(false);
+ setError(e);
+ });
}, []);
if (loading) return ;
+ if (error) return {error.toString()};
return (
@@ -246,7 +248,7 @@ function App() {
to="/dashboard"
icon={
}
/>
-
+
Issued Assets
rpc::validator_node_
.map_err(|e| Status::internal(format!("Could not create state db: {}", e)))?
{
let mut state_db_reader = state.reader();
+ let instruction = Instruction::new(template_id, request.method, request.args);
let response_bytes = self
.asset_processor
- .invoke_read_method(template_id, request.method, &request.args, &mut state_db_reader)
+ .invoke_read_method(&instruction, &mut state_db_reader)
.map_err(|e| Status::internal(format!("Could not invoke read method: {}", e)))?;
Ok(Response::new(rpc::InvokeReadMethodResponse {
result: response_bytes.unwrap_or_default(),
diff --git a/applications/tari_validator_node/src/p2p/rpc/service_impl.rs b/applications/tari_validator_node/src/p2p/rpc/service_impl.rs
index 9a7d4947bf0..bf73986a861 100644
--- a/applications/tari_validator_node/src/p2p/rpc/service_impl.rs
+++ b/applications/tari_validator_node/src/p2p/rpc/service_impl.rs
@@ -20,7 +20,7 @@
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
-use std::convert::TryFrom;
+use std::convert::{TryFrom, TryInto};
use log::*;
use tari_common_types::types::PublicKey;
@@ -30,7 +30,7 @@ use tari_comms::{
};
use tari_crypto::tari_utilities::ByteArray;
use tari_dan_core::{
- models::{Instruction, TemplateId, TreeNodeHash},
+ models::{Instruction, TreeNodeHash},
services::{AssetProcessor, MempoolService},
storage::{state::StateDbUnitOfWorkReader, DbFactory},
};
@@ -93,14 +93,18 @@ where
.ok_or_else(|| RpcStatus::not_found("This node does not process this asset".to_string()))?;
let mut unit_of_work = state.reader();
+
+ let instruction = Instruction::new(
+ request
+ .template_id
+ .try_into()
+ .map_err(|_| RpcStatus::bad_request("Invalid template_id"))?,
+ request.method,
+ request.args,
+ );
let response_bytes = self
.asset_processor
- .invoke_read_method(
- TemplateId::try_from(request.template_id).map_err(|_| RpcStatus::bad_request("Invalid template_id"))?,
- request.method,
- &request.args,
- &mut unit_of_work,
- )
+ .invoke_read_method(&instruction, &mut unit_of_work)
.map_err(|e| RpcStatus::general(format!("Could not invoke read method: {}", e)))?;
Ok(Response::new(proto::InvokeReadMethodResponse {
@@ -115,7 +119,10 @@ where
dbg!(&request);
let request = request.into_message();
let instruction = Instruction::new(
- TemplateId::try_from(request.template_id).map_err(|_| RpcStatus::bad_request("Invalid template_id"))?,
+ request
+ .template_id
+ .try_into()
+ .map_err(|_| RpcStatus::bad_request("Invalid template_id"))?,
request.method.clone(),
request.args.clone(),
/* TokenId(request.token_id.clone()),
diff --git a/applications/tari_validator_node/src/p2p/services/inbound_connection_service.rs b/applications/tari_validator_node/src/p2p/services/inbound_connection_service.rs
index ba610ac9ce8..0919ca9fd92 100644
--- a/applications/tari_validator_node/src/p2p/services/inbound_connection_service.rs
+++ b/applications/tari_validator_node/src/p2p/services/inbound_connection_service.rs
@@ -156,25 +156,29 @@ impl TariCommsInboundConnectionService {
} => {
// Check for already received messages
let mut indexes_to_remove = vec![];
- let now = Instant::now();
let mut result_message = None;
- for (index, message) in self.buffered_messages.iter().enumerate() {
- if now - message.2 > self.expiry_time {
- warn!(target: LOG_TARGET, "Message has expired: {:?}", message);
+ for (index, (from_pk, message, msg_time)) in self.buffered_messages.iter().enumerate() {
+ if msg_time.elapsed() > self.expiry_time {
+ warn!(
+ target: LOG_TARGET,
+ "Message has expired: ({:.2?}) {:?}",
+ msg_time.elapsed(),
+ message
+ );
indexes_to_remove.push(index);
} else {
match wait_for_type {
WaitForMessageType::Message => {
- if message.1.message_type() == message_type && message.1.view_number() == view_number {
- result_message = Some((message.0.clone(), message.1.clone()));
+ if message.message_type() == message_type && message.view_number() == view_number {
+ result_message = Some((from_pk.clone(), message.clone()));
indexes_to_remove.push(index);
break;
}
},
WaitForMessageType::QuorumCertificate => {
- if let Some(qc) = message.1.justify() {
+ if let Some(qc) = message.justify() {
if qc.message_type() == message_type && qc.view_number() == view_number {
- result_message = Some((message.0.clone(), message.1.clone()));
+ result_message = Some((from_pk.clone(), message.clone()));
indexes_to_remove.push(index);
break;
}
@@ -256,10 +260,10 @@ impl TariCommsInboundConnectionService {
"Found waiter for this message, waking task... {:?}",
message.message_type()
);
- if let Some(w) = self.waiters.swap_remove_back(index) {
+ if let Some((_, _, _, reply)) = self.waiters.swap_remove_back(index) {
// The receiver on the other end of this channel may have dropped naturally
// as it moves out of scope and is not longer interested in receiving the message
- if w.3.send((from.clone(), message.clone())).is_ok() {
+ if reply.send((from.clone(), message.clone())).is_ok() {
return Ok(());
}
}
@@ -291,7 +295,10 @@ impl TariCommsInboundReceiverHandle {
}
#[async_trait]
-impl InboundConnectionService for TariCommsInboundReceiverHandle {
+impl InboundConnectionService for TariCommsInboundReceiverHandle {
+ type Addr = CommsPublicKey;
+ type Payload = TariDanPayload;
+
async fn wait_for_message(
&self,
message_type: HotStuffMessageType,
diff --git a/applications/tari_validator_node/src/p2p/services/outbound_connection_service.rs b/applications/tari_validator_node/src/p2p/services/outbound_connection_service.rs
index f90935f3b1c..99ca3257221 100644
--- a/applications/tari_validator_node/src/p2p/services/outbound_connection_service.rs
+++ b/applications/tari_validator_node/src/p2p/services/outbound_connection_service.rs
@@ -37,6 +37,8 @@ use tokio::sync::mpsc::Sender;
use crate::p2p::proto;
+const LOG_TARGET: &str = "tari::validator_node::messages::outbound::validator_node";
+
pub struct TariCommsOutboundService {
outbound_message_requester: OutboundMessageRequester,
loopback_service: Sender<(CommsPublicKey, HotStuffMessage)>,
@@ -61,16 +63,25 @@ impl TariCommsOutboundService {
}
#[async_trait]
-impl OutboundService for TariCommsOutboundService {
+impl OutboundService for TariCommsOutboundService {
+ type Addr = CommsPublicKey;
+ type Payload = TariDanPayload;
+
async fn send(
&mut self,
from: CommsPublicKey,
to: CommsPublicKey,
message: HotStuffMessage,
) -> Result<(), DigitalAssetError> {
- debug!(target: "messages::outbound::validator_node", "Outbound message to be sent:{} {:?}", to, message);
+ debug!(target: LOG_TARGET, "Outbound message to be sent:{} {:?}", to, message);
// Tari comms does allow sending to itself
if from == to && message.asset_public_key() == &self.asset_public_key {
+ debug!(
+ target: LOG_TARGET,
+ "Sending {:?} to self for asset {}",
+ message.message_type(),
+ message.asset_public_key()
+ );
self.loopback_service.send((from, message)).await.unwrap();
return Ok(());
}
diff --git a/common/src/configuration/validator_node_config.rs b/common/src/configuration/validator_node_config.rs
index 6ca54043b84..cf3895c1965 100644
--- a/common/src/configuration/validator_node_config.rs
+++ b/common/src/configuration/validator_node_config.rs
@@ -41,11 +41,20 @@ pub struct ValidatorNodeConfig {
pub base_node_grpc_address: SocketAddr,
#[serde(default = "default_wallet_grpc_address")]
pub wallet_grpc_address: SocketAddr,
+ #[serde(default = "default_true")]
pub scan_for_assets: bool,
+ #[serde(default = "default_asset_scanning_interval")]
pub new_asset_scanning_interval: u64,
pub assets_allow_list: Option>,
}
+fn default_true() -> bool {
+ true
+}
+fn default_asset_scanning_interval() -> u64 {
+ 10
+}
+
fn default_asset_config_directory() -> PathBuf {
PathBuf::from("assets")
}
diff --git a/dan_layer/core/src/digital_assets_error.rs b/dan_layer/core/src/digital_assets_error.rs
index 65fcf6d0720..e697c1a823b 100644
--- a/dan_layer/core/src/digital_assets_error.rs
+++ b/dan_layer/core/src/digital_assets_error.rs
@@ -76,6 +76,14 @@ pub enum DigitalAssetError {
StateSyncError(#[from] StateSyncError),
#[error("Validator node client error: {0}")]
ValidatorNodeClientError(#[from] ValidatorNodeClientError),
+ #[error("Peer did not send a quorum certificate in prepare phase")]
+ PreparePhaseNoQuorumCertificate,
+ #[error("Quorum certificate does not extend node")]
+ PreparePhaseCertificateDoesNotExtendNode,
+ #[error("Node not safe")]
+ PreparePhaseNodeNotSafe,
+ #[error("Unsupported template method {name}")]
+ TemplateUnsupportedMethod { name: String },
}
impl From for DigitalAssetError {
diff --git a/dan_layer/core/src/fixed_hash.rs b/dan_layer/core/src/fixed_hash.rs
index ea642e2c3fb..400bb5d6c22 100644
--- a/dan_layer/core/src/fixed_hash.rs
+++ b/dan_layer/core/src/fixed_hash.rs
@@ -31,7 +31,7 @@ const ZERO_HASH: [u8; FixedHash::byte_size()] = [0u8; FixedHash::byte_size()];
#[error("Invalid size")]
pub struct FixedHashSizeError;
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct FixedHash([u8; FixedHash::byte_size()]);
impl FixedHash {
diff --git a/dan_layer/core/src/models/hot_stuff_tree_node.rs b/dan_layer/core/src/models/hot_stuff_tree_node.rs
index 19d319ad16b..9478168c22b 100644
--- a/dan_layer/core/src/models/hot_stuff_tree_node.rs
+++ b/dan_layer/core/src/models/hot_stuff_tree_node.rs
@@ -47,12 +47,12 @@ impl HotStuffTreeNode {
s
}
- pub fn genesis(payload: TPayload) -> HotStuffTreeNode {
+ pub fn genesis(payload: TPayload, state_root: StateRoot) -> HotStuffTreeNode {
let mut s = Self {
parent: TreeNodeHash::zero(),
payload,
hash: TreeNodeHash::zero(),
- state_root: StateRoot::initial(),
+ state_root,
height: 0,
};
s.hash = s.calculate_hash();
diff --git a/dan_layer/core/src/models/instruction_set.rs b/dan_layer/core/src/models/instruction_set.rs
index 1e9d6a7cd08..0ef6a50b225 100644
--- a/dan_layer/core/src/models/instruction_set.rs
+++ b/dan_layer/core/src/models/instruction_set.rs
@@ -20,15 +20,24 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-use std::{hash::Hash, iter::FromIterator};
+use std::{convert::TryFrom, hash::Hash, iter::FromIterator};
use tari_crypto::common::Blake256;
use tari_mmr::MerkleMountainRange;
-use crate::models::{ConsensusHash, Instruction};
+use crate::{
+ fixed_hash::FixedHash,
+ models::{ConsensusHash, Instruction},
+};
#[derive(PartialEq, Clone, Debug, Hash)]
-pub struct InstructionSetHash(Vec);
+pub struct InstructionSetHash(FixedHash);
+
+impl InstructionSetHash {
+ pub fn zero() -> InstructionSetHash {
+ Self(FixedHash::zero())
+ }
+}
impl InstructionSetHash {
pub fn as_bytes(&self) -> &[u8] {
@@ -36,6 +45,12 @@ impl InstructionSetHash {
}
}
+impl From for InstructionSetHash {
+ fn from(hash: FixedHash) -> Self {
+ Self(hash)
+ }
+}
+
// TODO: Implement hash properly
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Clone, Debug)]
@@ -52,7 +67,7 @@ impl InstructionSet {
pub fn from_vec(instructions: Vec) -> Self {
let mut result = Self {
instructions,
- hash: InstructionSetHash(vec![]),
+ hash: InstructionSetHash::zero(),
};
result.hash = result.calculate_hash();
result
@@ -65,7 +80,7 @@ impl InstructionSet {
mmr.push(instruction.calculate_hash().to_vec()).unwrap();
}
- InstructionSetHash(mmr.get_merkle_root().unwrap())
+ FixedHash::try_from(mmr.get_merkle_root().unwrap()).unwrap().into()
}
pub fn instructions(&self) -> &[Instruction] {
@@ -91,3 +106,19 @@ impl ConsensusHash for InstructionSet {
self.hash.as_bytes()
}
}
+
+impl IntoIterator for InstructionSet {
+ type IntoIter = as IntoIterator>::IntoIter;
+ type Item = Instruction;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.instructions.into_iter()
+ }
+}
+
+impl Extend for InstructionSet {
+ fn extend>(&mut self, iter: T) {
+ self.instructions.extend(iter);
+ self.hash = self.calculate_hash();
+ }
+}
diff --git a/dan_layer/core/src/models/view_id.rs b/dan_layer/core/src/models/view_id.rs
index bc60c079ed1..0f9459e3c3c 100644
--- a/dan_layer/core/src/models/view_id.rs
+++ b/dan_layer/core/src/models/view_id.rs
@@ -23,7 +23,7 @@
use std::{
cmp::Ordering,
fmt::{self, Display},
- ops::Sub,
+ ops::{Add, Sub},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -34,6 +34,10 @@ impl ViewId {
(self.0 % committee_size as u64) as usize
}
+ pub fn is_genesis(&self) -> bool {
+ self.0 == 0
+ }
+
pub fn next(&self) -> ViewId {
ViewId(self.0 + 1)
}
@@ -41,6 +45,10 @@ impl ViewId {
pub fn as_u64(&self) -> u64 {
self.0
}
+
+ pub fn saturating_sub(self, other: ViewId) -> ViewId {
+ self.0.saturating_sub(other.0).into()
+ }
}
impl PartialOrd for ViewId {
@@ -61,6 +69,14 @@ impl Display for ViewId {
}
}
+impl Add for ViewId {
+ type Output = ViewId;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ ViewId(self.0 + rhs.0)
+ }
+}
+
impl Sub for ViewId {
type Output = ViewId;
diff --git a/dan_layer/core/src/services/asset_processor.rs b/dan_layer/core/src/services/asset_processor.rs
index 8d15ebd3ca1..7015e28c97c 100644
--- a/dan_layer/core/src/services/asset_processor.rs
+++ b/dan_layer/core/src/services/asset_processor.rs
@@ -26,20 +26,13 @@ use tari_core::transactions::transaction_components::TemplateParameter;
use crate::{
digital_assets_error::DigitalAssetError,
- models::{AssetDefinition, Instruction, TemplateId},
+ models::{Instruction, InstructionSet, TemplateId},
storage::state::{StateDbUnitOfWork, StateDbUnitOfWorkReader},
template_command::ExecutionResult,
templates::{tip002_template, tip004_template, tip721_template},
};
pub trait AssetProcessor: Sync + Send + 'static {
- fn init_template(
- &self,
- template_parameter: &TemplateParameter,
- asset_definition: &AssetDefinition,
- state_db: &mut TUnitOfWork,
- ) -> Result<(), DigitalAssetError>;
-
// purposefully made sync, because instructions should be run in order, and complete before the
// next one starts. There may be a better way to enforce this though...
fn execute_instruction(
@@ -50,10 +43,8 @@ pub trait AssetProcessor: Sync + Send + 'static {
fn invoke_read_method(
&self,
- template_id: TemplateId,
- method: String,
- args: &[u8],
- state_db: &mut TUnitOfWorkReader,
+ instruction: &Instruction,
+ state_db: &TUnitOfWorkReader,
) -> Result