mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-03-18 09:53:22 +00:00
feat: add restriction clippy lints
This commit is contained in:
parent
efe8393ba0
commit
e3b93b6e9a
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5561,6 +5561,7 @@ name = "nssa"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"amm_core",
|
||||
"anyhow",
|
||||
"borsh",
|
||||
"env_logger",
|
||||
"hex",
|
||||
|
||||
91
Cargo.toml
91
Cargo.toml
@ -147,6 +147,8 @@ warnings = "deny"
|
||||
|
||||
[workspace.lints]
|
||||
clippy.all = { level = "deny", priority = -1 }
|
||||
|
||||
# Pedantic
|
||||
clippy.pedantic = { level = "deny", priority = -1 }
|
||||
|
||||
# Reason: documenting every function returning Result is too verbose and doesn't add much value when you have good error types.
|
||||
@ -157,5 +159,92 @@ clippy.missing-panics-doc = "allow"
|
||||
clippy.similar-names = "allow"
|
||||
# Reason: this lint is too strict and hard to fix.
|
||||
clippy.too-many-lines = "allow"
|
||||
# Reason: std hasher is fine for us in public functions
|
||||
# Reason: std hasher is fine for us in public functions.
|
||||
clippy.implicit-hasher = "allow"
|
||||
|
||||
# Restriction
|
||||
clippy.restriction = { level = "deny", priority = -1 }
|
||||
|
||||
# Reason: we deny the whole `restriction` group but we allow things that don't make sense for us.
|
||||
# That way we can still benefit from new lints added to the `restriction` group without having to
|
||||
# explicitly allow them.
|
||||
# As a downside our contributors don't know if some lint was enabled intentionally or just no one
|
||||
# else faced it before to allow it but we can handle this during code reviews.
|
||||
clippy.blanket-clippy-restriction-lints = "allow"
|
||||
# Reason: we can't avoid using unwrap for now.
|
||||
clippy.unwrap-used = "allow"
|
||||
# Reason: we can't avoid using expect for now.
|
||||
clippy.expect-used = "allow"
|
||||
# Reason: unreachable is good in many cases.
|
||||
clippy.unreachable = "allow"
|
||||
# Reason: this is ridiculous strict in our codebase and doesn't add any value.
|
||||
clippy.single-call-fn = "allow"
|
||||
# Reason: we use panic in some places and it's okay.
|
||||
clippy.panic = "allow"
|
||||
# Reason: shadowing is good most of the times.
|
||||
clippy.shadow-reuse = "allow"
|
||||
# Reason: implicit return is good.
|
||||
clippy.implicit-return = "allow"
|
||||
# Reason: std is fine for us, we don't need to use core.
|
||||
clippy.std-instead-of-core = "allow"
|
||||
# Reason: std is fine for us, we don't need to use alloc.
|
||||
clippy.std-instead-of-alloc = "allow"
|
||||
# Reason: default methods are good most of the time.
|
||||
clippy.missing-trait-methods = "allow"
|
||||
# Reason: this is too verbose and doesn't help much if you have rust analyzer.
|
||||
clippy.pattern-type-mismatch = "allow"
|
||||
# Reason: decreases readability.
|
||||
clippy.assertions-on-result-states = "allow"
|
||||
# Reason: documenting every assert is too verbose.
|
||||
clippy.missing-assert-message = "allow"
|
||||
# Reason: documenting private items is too verbose and doesn't add much value.
|
||||
clippy.missing-docs-in-private-items = "allow"
|
||||
# Reason: we use separated suffix style.
|
||||
clippy.separated_literal_suffix = "allow"
|
||||
# Reason: sometimes absolute paths are more readable.
|
||||
clippy.absolute-paths = "allow"
|
||||
# Reason: sometimes it's as readable as full variable naming.
|
||||
clippy.min-ident-chars = "allow"
|
||||
# Reason: it's very common and handy.
|
||||
clippy.indexing-slicing = "allow"
|
||||
# Reason: we use little endian style.
|
||||
clippy.little-endian-bytes = "allow"
|
||||
# Reason: we use this style of pub visibility.
|
||||
clippy.pub-with-shorthand = "allow"
|
||||
# Reason: question mark operator is very cool.
|
||||
clippy.question-mark-used = "allow"
|
||||
# Reason: it's fine to panic in tests and some functions where it makes sense.
|
||||
clippy.panic-in-result-fn = "allow"
|
||||
# Reason: we don't care that much about inlining and LTO should take care of it.
|
||||
clippy.missing_inline_in_public_items = "allow"
|
||||
# Reason: it's okay for us.
|
||||
clippy.default-numeric-fallback = "allow"
|
||||
# Reason: this is fine for us.
|
||||
clippy.exhaustive-enums = "allow"
|
||||
# Reason: this is fine for us.
|
||||
clippy.exhaustive-structs = "allow"
|
||||
# Reason: this helps readability when item is imported in other modules.
|
||||
clippy.module-name-repetitions = "allow"
|
||||
# Reason: mostly historical reasons, maybe we'll address this in future.
|
||||
clippy.mod-module-files = "allow"
|
||||
# Reason: named module files is our preferred way.
|
||||
clippy.self-named-module-files = "allow"
|
||||
# Reason: this is actually quite handy.
|
||||
clippy.impl-trait-in-params = "allow"
|
||||
# Reason: this is often useful.
|
||||
clippy.use-debug = "allow"
|
||||
# Reason: this is sometimes useful.
|
||||
clippy.field-scoped-visibility-modifiers = "allow"
|
||||
# Reason: `pub use` is good for re-exports and hiding unnecessary details.
|
||||
clippy.pub-use = "allow"
|
||||
# Reason: we prefer semicolons inside blocks.
|
||||
clippy.semicolon-outside-block = "allow"
|
||||
# Reason: we don't do it blindly, this is mostly internal constraints checks.
|
||||
clippy.unwrap-in-result = "allow"
|
||||
# Reason: we don't see any problems with that.
|
||||
clippy.shadow-same = "allow"
|
||||
# Reason: this lint is too verbose.
|
||||
clippy.let-underscore-untyped = "allow"
|
||||
# Reason: this lint is actually bad as it forces to use wildcard `..` instead of
|
||||
# field-by-field `_` which may lead to subtle bugs when new fields are added to the struct.
|
||||
clippy.unneeded-field-pattern = "allow"
|
||||
|
||||
@ -2,7 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use common::config::BasicAuth;
|
||||
use futures::{Stream, TryFutureExt};
|
||||
use futures::{Stream, TryFutureExt as _};
|
||||
#[expect(clippy::single_component_path_imports, reason = "Satisfy machete")]
|
||||
use humantime_serde;
|
||||
use log::{info, warn};
|
||||
|
||||
54
clippy.toml
Normal file
54
clippy.toml
Normal file
@ -0,0 +1,54 @@
|
||||
module-item-order-groupings = [
|
||||
[
|
||||
"use",
|
||||
[
|
||||
"use",
|
||||
],
|
||||
],
|
||||
[
|
||||
"modules",
|
||||
[
|
||||
"extern_crate",
|
||||
"mod",
|
||||
"foreign_mod",
|
||||
],
|
||||
],
|
||||
[
|
||||
"macros",
|
||||
[
|
||||
"macro",
|
||||
],
|
||||
],
|
||||
[
|
||||
"global_asm",
|
||||
[
|
||||
"global_asm",
|
||||
],
|
||||
],
|
||||
[
|
||||
"UPPER_SNAKE_CASE",
|
||||
[
|
||||
"static",
|
||||
"const",
|
||||
],
|
||||
],
|
||||
[
|
||||
"PascalCase",
|
||||
[
|
||||
"ty_alias",
|
||||
"enum",
|
||||
"struct",
|
||||
"union",
|
||||
"trait",
|
||||
"trait_alias",
|
||||
"impl",
|
||||
],
|
||||
],
|
||||
[
|
||||
"lower_snake_case",
|
||||
[
|
||||
"fn",
|
||||
],
|
||||
],
|
||||
]
|
||||
source-item-ordering = ["module"]
|
||||
@ -1,7 +1,7 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa::AccountId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256, digest::FixedOutput};
|
||||
use sha2::{Digest as _, Sha256, digest::FixedOutput as _};
|
||||
|
||||
use crate::{HashType, transaction::NSSATransaction};
|
||||
|
||||
@ -20,7 +20,7 @@ pub struct BlockMeta {
|
||||
#[derive(Debug, Clone)]
|
||||
/// Our own hasher.
|
||||
/// Currently it is SHA256 hasher wrapper. May change in a future.
|
||||
pub struct OwnHasher {}
|
||||
pub struct OwnHasher;
|
||||
|
||||
impl OwnHasher {
|
||||
fn hash(data: &[u8]) -> HashType {
|
||||
@ -130,7 +130,7 @@ mod tests {
|
||||
use crate::{HashType, block::HashableBlockData, test_utils};
|
||||
|
||||
#[test]
|
||||
fn test_encoding_roundtrip() {
|
||||
fn encoding_roundtrip() {
|
||||
let transactions = vec![test_utils::produce_dummy_empty_transaction()];
|
||||
let block = test_utils::produce_dummy_block(1, Some(HashType([1; 32])), transactions);
|
||||
let hashable = HashableBlockData::from(block);
|
||||
|
||||
@ -42,7 +42,7 @@ impl FromStr for BasicAuth {
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
username: username.to_string(),
|
||||
username: username.to_owned(),
|
||||
password: password.map(std::string::ToString::to_string),
|
||||
})
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ impl From<SequencerRpcError> for SequencerClientError {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ExecutionFailureKind {
|
||||
#[error("Failed to get account data from sequencer")]
|
||||
SequencerError,
|
||||
#[error("Failed to get data from sequencer")]
|
||||
SequencerError(#[source] anyhow::Error),
|
||||
#[error("Inputs amounts does not match outputs")]
|
||||
AmountMismatchError,
|
||||
#[error("Accounts key not found")]
|
||||
|
||||
@ -46,7 +46,7 @@ impl FromStr for HashType {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut bytes = [0u8; 32];
|
||||
let mut bytes = [0_u8; 32];
|
||||
hex::decode_to_slice(s, &mut bytes)?;
|
||||
Ok(HashType(bytes))
|
||||
}
|
||||
@ -90,7 +90,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn serialization_roundtrip() {
|
||||
let original = HashType([1u8; 32]);
|
||||
let original = HashType([1_u8; 32]);
|
||||
let serialized = original.to_string();
|
||||
let deserialized = HashType::from_str(&serialized).unwrap();
|
||||
assert_eq!(original, deserialized);
|
||||
|
||||
@ -5,10 +5,10 @@ use serde_json::{Value, to_value};
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct RpcParseError(pub String);
|
||||
|
||||
#[allow(clippy::too_long_first_doc_paragraph)]
|
||||
/// This struct may be returned from JSON RPC server in case of error
|
||||
/// This struct may be returned from JSON RPC server in case of error.
|
||||
///
|
||||
/// It is expected that that this struct has impls From<_> all other RPC errors
|
||||
/// like [`RpcBlockError`](crate::types::blocks::RpcBlockError)
|
||||
/// like [`RpcBlockError`](crate::types::blocks::RpcBlockError).
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RpcError {
|
||||
@ -167,6 +167,7 @@ impl From<RpcParseError> for RpcError {
|
||||
|
||||
impl From<std::convert::Infallible> for RpcError {
|
||||
fn from(_: std::convert::Infallible) -> Self {
|
||||
// SAFETY: Infallible error can never be constructed, so this code can never be reached.
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,14 @@ use std::fmt::{Formatter, Result as FmtResult};
|
||||
|
||||
use serde::{
|
||||
de::{Deserializer, Error, Unexpected, Visitor},
|
||||
ser::{SerializeStruct, Serializer},
|
||||
ser::{SerializeStruct as _, Serializer},
|
||||
};
|
||||
use serde_json::{Result as JsonResult, Value};
|
||||
|
||||
use super::errors::RpcError;
|
||||
|
||||
pub type Parsed = Result<Message, Broken>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct Version;
|
||||
|
||||
@ -29,9 +31,12 @@ impl serde::Serialize for Version {
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Version {
|
||||
#[expect(
|
||||
clippy::renamed_function_params,
|
||||
reason = "More readable than original serde parameter names"
|
||||
)]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct VersionVisitor;
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
impl Visitor<'_> for VersionVisitor {
|
||||
type Value = Version;
|
||||
|
||||
@ -53,6 +58,10 @@ impl<'de> serde::Deserialize<'de> for Version {
|
||||
/// An RPC request.
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
pub struct Request {
|
||||
jsonrpc: Version,
|
||||
pub method: String,
|
||||
@ -99,6 +108,10 @@ impl Request {
|
||||
/// A response to an RPC.
|
||||
///
|
||||
/// It is created by the methods on [Request](struct.Request.html).
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Response {
|
||||
jsonrpc: Version,
|
||||
@ -110,30 +123,22 @@ impl serde::Serialize for Response {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut sub = serializer.serialize_struct("Response", 3)?;
|
||||
sub.serialize_field("jsonrpc", &self.jsonrpc)?;
|
||||
match self.result {
|
||||
Ok(ref value) => sub.serialize_field("result", value),
|
||||
Err(ref err) => sub.serialize_field("error", err),
|
||||
match &self.result {
|
||||
Ok(value) => sub.serialize_field("result", value),
|
||||
Err(err) => sub.serialize_field("error", err),
|
||||
}?;
|
||||
sub.serialize_field("id", &self.id)?;
|
||||
sub.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializer for `Option<Value>` that produces `Some(Value::Null)`.
|
||||
///
|
||||
/// The usual one produces None in that case. But we need to know the difference between
|
||||
/// `{x: null}` and `{}`.
|
||||
fn some_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Value>, D::Error> {
|
||||
serde::Deserialize::deserialize(deserializer).map(Some)
|
||||
}
|
||||
|
||||
/// A helper trick for deserialization.
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct WireResponse {
|
||||
// It is actually used to eat and sanity check the deserialized text
|
||||
#[allow(dead_code)]
|
||||
jsonrpc: Version,
|
||||
#[serde(rename = "jsonrpc")]
|
||||
_jsonrpc: Version,
|
||||
// Make sure we accept null as Some(Value::Null), instead of going to None
|
||||
#[serde(default, deserialize_with = "some_value")]
|
||||
result: Option<Value>,
|
||||
@ -164,6 +169,10 @@ impl<'de> serde::Deserialize<'de> for Response {
|
||||
}
|
||||
|
||||
/// A notification (doesn't expect an answer).
|
||||
#[expect(
|
||||
clippy::partial_pub_fields,
|
||||
reason = "We don't want to allow access to the version, but the others are public for ease of use"
|
||||
)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Notification {
|
||||
@ -261,11 +270,24 @@ impl Message {
|
||||
pub fn id(&self) -> Value {
|
||||
match self {
|
||||
Message::Request(req) => req.id.clone(),
|
||||
_ => Value::Null,
|
||||
Message::Response(response) => response.id.clone(),
|
||||
Message::Notification(_) | Message::Batch(_) | Message::UnmatchedSub(_) => Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for String {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_string(&val).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for Vec<u8> {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_vec(&val).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A broken message.
|
||||
///
|
||||
/// Protocol-level errors.
|
||||
@ -286,11 +308,11 @@ impl Broken {
|
||||
/// with the right values.
|
||||
#[must_use]
|
||||
pub fn reply(&self) -> Message {
|
||||
match *self {
|
||||
match self {
|
||||
Broken::Unmatched(_) => Message::error(RpcError::parse_error(
|
||||
"JSON RPC Request format was expected".to_owned(),
|
||||
)),
|
||||
Broken::SyntaxError(ref e) => Message::error(RpcError::parse_error(e.clone())),
|
||||
Broken::SyntaxError(e) => Message::error(RpcError::parse_error(e.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,8 +334,6 @@ pub fn decoded_to_parsed(res: JsonResult<WireMessage>) -> Parsed {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Parsed = Result<Message, Broken>;
|
||||
|
||||
/// Read a [Message](enum.Message.html) from a slice.
|
||||
///
|
||||
/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html).
|
||||
@ -328,16 +348,12 @@ pub fn from_str(s: &str) -> Parsed {
|
||||
from_slice(s.as_bytes())
|
||||
}
|
||||
|
||||
impl From<Message> for String {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_string(&val).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for Vec<u8> {
|
||||
fn from(val: Message) -> Self {
|
||||
::serde_json::ser::to_vec(&val).unwrap()
|
||||
}
|
||||
/// Deserializer for `Option<Value>` that produces `Some(Value::Null)`.
|
||||
///
|
||||
/// The usual one produces None in that case. But we need to know the difference between
|
||||
/// `{x: null}` and `{}`.
|
||||
fn some_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Value>, D::Error> {
|
||||
serde::Deserialize::deserialize(deserializer).map(Some)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -475,7 +491,6 @@ mod tests {
|
||||
///
|
||||
/// The reject is done by returning it as Unmatched.
|
||||
#[test]
|
||||
#[allow(clippy::panic)]
|
||||
fn broken() {
|
||||
// A helper with one test
|
||||
fn one(input: &str) {
|
||||
@ -499,7 +514,7 @@ mod tests {
|
||||
// Something completely different
|
||||
one(r#"{"x": [1, 2, 3]}"#);
|
||||
|
||||
match from_str(r"{]") {
|
||||
match from_str("{]") {
|
||||
Err(Broken::SyntaxError(_)) => (),
|
||||
other => panic!("Something unexpected: {other:?}"),
|
||||
}
|
||||
@ -510,7 +525,6 @@ mod tests {
|
||||
/// This doesn't have a full coverage, because there's not much to actually test there.
|
||||
/// Most of it is related to the ids.
|
||||
#[test]
|
||||
#[allow(clippy::panic)]
|
||||
#[ignore = "Not a full coverage test"]
|
||||
fn constructors() {
|
||||
let msg1 = Message::request("call".to_owned(), json!([1, 2, 3]));
|
||||
@ -528,9 +542,9 @@ mod tests {
|
||||
};
|
||||
let id1 = req1.id.clone();
|
||||
// When we answer a message, we get the same ID
|
||||
if let Message::Response(ref resp) = req1.reply(json!([1, 2, 3])) {
|
||||
if let Message::Response(resp) = req1.reply(json!([1, 2, 3])) {
|
||||
assert_eq!(
|
||||
*resp,
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Ok(json!([1, 2, 3])),
|
||||
@ -542,11 +556,9 @@ mod tests {
|
||||
}
|
||||
let id2 = req2.id.clone();
|
||||
// The same with an error
|
||||
if let Message::Response(ref resp) =
|
||||
req2.error(RpcError::new(42, "Wrong!".to_owned(), None))
|
||||
{
|
||||
if let Message::Response(resp) = req2.error(RpcError::new(42, "Wrong!".to_owned(), None)) {
|
||||
assert_eq!(
|
||||
*resp,
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(RpcError::new(42, "Wrong!".to_owned(), None)),
|
||||
@ -557,11 +569,11 @@ mod tests {
|
||||
panic!("Not a response");
|
||||
}
|
||||
// When we have unmatched, we generate a top-level error with Null id.
|
||||
if let Message::Response(ref resp) =
|
||||
if let Message::Response(resp) =
|
||||
Message::error(RpcError::new(43, "Also wrong!".to_owned(), None))
|
||||
{
|
||||
assert_eq!(
|
||||
*resp,
|
||||
resp,
|
||||
Response {
|
||||
jsonrpc: Version,
|
||||
result: Err(RpcError::new(43, "Also wrong!".to_owned(), None)),
|
||||
|
||||
@ -3,6 +3,17 @@ use serde_json::Value;
|
||||
|
||||
use super::errors::RpcParseError;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! parse_request {
|
||||
($request_name:ty) => {
|
||||
impl RpcRequest for $request_name {
|
||||
fn parse(value: Option<Value>) -> Result<Self, RpcParseError> {
|
||||
parse_params::<Self>(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait RpcRequest: Sized {
|
||||
fn parse(value: Option<Value>) -> Result<Self, RpcParseError>;
|
||||
}
|
||||
@ -15,13 +26,3 @@ pub fn parse_params<T: DeserializeOwned>(value: Option<Value>) -> Result<T, RpcP
|
||||
Err(RpcParseError("Require at least one parameter".to_owned()))
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! parse_request {
|
||||
($request_name:ty) => {
|
||||
impl RpcRequest for $request_name {
|
||||
fn parse(value: Option<Value>) -> Result<Self, RpcParseError> {
|
||||
parse_params::<Self>(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -11,8 +11,62 @@ use super::{
|
||||
};
|
||||
use crate::{HashType, parse_request};
|
||||
|
||||
mod base64_deser {
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer, ser::SerializeSeq as _};
|
||||
|
||||
pub mod vec {
|
||||
use super::*;
|
||||
|
||||
pub fn serialize<S>(bytes_vec: &[Vec<u8>], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(bytes_vec.len()))?;
|
||||
for bytes in bytes_vec {
|
||||
let s = general_purpose::STANDARD.encode(bytes);
|
||||
seq.serialize_element(&s)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_strings: Vec<String> = Deserialize::deserialize(deserializer)?;
|
||||
base64_strings
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
general_purpose::STANDARD
|
||||
.decode(&s)
|
||||
.map_err(serde::de::Error::custom)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let base64_string = general_purpose::STANDARD.encode(bytes);
|
||||
serializer.serialize_str(&base64_string)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_string: String = Deserialize::deserialize(deserializer)?;
|
||||
general_purpose::STANDARD
|
||||
.decode(&base64_string)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloRequest {}
|
||||
pub struct HelloRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RegisterAccountRequest {
|
||||
@ -38,13 +92,13 @@ pub struct GetBlockRangeDataRequest {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetGenesisIdRequest {}
|
||||
pub struct GetGenesisIdRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetLastBlockRequest {}
|
||||
pub struct GetLastBlockRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetInitialTestnetAccountsRequest {}
|
||||
pub struct GetInitialTestnetAccountsRequest;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountBalanceRequest {
|
||||
@ -72,7 +126,7 @@ pub struct GetProofForCommitmentRequest {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProgramIdsRequest {}
|
||||
pub struct GetProgramIdsRequest;
|
||||
|
||||
parse_request!(HelloRequest);
|
||||
parse_request!(RegisterAccountRequest);
|
||||
@ -117,60 +171,6 @@ pub struct GetBlockRangeDataResponse {
|
||||
pub blocks: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
mod base64_deser {
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer, ser::SerializeSeq as _};
|
||||
|
||||
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let base64_string = general_purpose::STANDARD.encode(bytes);
|
||||
serializer.serialize_str(&base64_string)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_string: String = Deserialize::deserialize(deserializer)?;
|
||||
general_purpose::STANDARD
|
||||
.decode(&base64_string)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
pub mod vec {
|
||||
use super::*;
|
||||
|
||||
pub fn serialize<S>(bytes_vec: &[Vec<u8>], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(bytes_vec.len()))?;
|
||||
for bytes in bytes_vec {
|
||||
let s = general_purpose::STANDARD.encode(bytes);
|
||||
seq.serialize_element(&s)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base64_strings: Vec<String> = Deserialize::deserialize(deserializer)?;
|
||||
base64_strings
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
general_purpose::STANDARD
|
||||
.decode(&s)
|
||||
.map_err(serde::de::Error::custom)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetGenesisIdResponse {
|
||||
pub genesis_id: u64,
|
||||
|
||||
@ -31,11 +31,12 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct SequencerRpcResponse {
|
||||
jsonrpc: String,
|
||||
#[serde(rename = "jsonrpc")]
|
||||
_jsonrpc: String,
|
||||
result: serde_json::Value,
|
||||
id: u64,
|
||||
#[serde(rename = "id")]
|
||||
_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -69,7 +70,7 @@ impl SequencerClient {
|
||||
payload: Value,
|
||||
) -> Result<Value, SequencerClientError> {
|
||||
let request =
|
||||
rpc_primitives::message::Request::from_payload_version_2_0(method.to_string(), payload);
|
||||
rpc_primitives::message::Request::from_payload_version_2_0(method.to_owned(), payload);
|
||||
|
||||
log::debug!(
|
||||
"Calling method {method} with payload {request:?} to sequencer at {}",
|
||||
|
||||
@ -31,7 +31,7 @@ pub fn produce_dummy_block(
|
||||
let block_data = HashableBlockData {
|
||||
block_id: id,
|
||||
prev_block_hash: prev_hash.unwrap_or_default(),
|
||||
timestamp: id * 100,
|
||||
timestamp: id.saturating_mul(100),
|
||||
transactions,
|
||||
};
|
||||
|
||||
|
||||
@ -21,10 +21,11 @@ use nssa_core::{
|
||||
// In case an input account is uninitialized, the program will claim it when
|
||||
// producing the post-state.
|
||||
|
||||
type Instruction = (u8, Vec<u8>);
|
||||
const WRITE_FUNCTION_ID: u8 = 0;
|
||||
const MOVE_DATA_FUNCTION_ID: u8 = 1;
|
||||
|
||||
type Instruction = (u8, Vec<u8>);
|
||||
|
||||
fn build_post_state(post_account: Account) -> AccountPostState {
|
||||
if post_account.program_owner == DEFAULT_PROGRAM_ID {
|
||||
// This produces a claim request
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
#![expect(
|
||||
clippy::print_stdout,
|
||||
reason = "This is an example program, it's fine to print to stdout"
|
||||
)]
|
||||
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
|
||||
@ -19,10 +19,11 @@ use wallet::{PrivacyPreservingAccount, WalletCore};
|
||||
// methods/guest/target/riscv32im-risc0-zkvm-elf/docker/hello_world_with_move_function.bin \
|
||||
// write-public Ds8q5PjLcKwwV97Zi7duhRVF9uwA2PuYMoLL7FwCzsXE Hola
|
||||
|
||||
type Instruction = (u8, Vec<u8>);
|
||||
const WRITE_FUNCTION_ID: u8 = 0;
|
||||
const MOVE_DATA_FUNCTION_ID: u8 = 1;
|
||||
|
||||
type Instruction = (u8, Vec<u8>);
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
/// Path to program binary
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
pub mod account_preview;
|
||||
pub mod block_preview;
|
||||
pub mod transaction_preview;
|
||||
|
||||
pub use account_preview::AccountPreview;
|
||||
pub use block_preview::BlockPreview;
|
||||
pub use transaction_preview::TransactionPreview;
|
||||
|
||||
pub mod account_preview;
|
||||
pub mod block_preview;
|
||||
pub mod transaction_preview;
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
//! Formatting utilities for the explorer
|
||||
|
||||
/// Format timestamp to human-readable string
|
||||
#[expect(
|
||||
clippy::integer_division,
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "We need to convert milliseconds to seconds, and this is the most straightforward way to do it"
|
||||
)]
|
||||
pub fn format_timestamp(timestamp: u64) -> String {
|
||||
let seconds = timestamp / 1000;
|
||||
let datetime = chrono::DateTime::from_timestamp(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#![expect(
|
||||
clippy::must_use_candidate,
|
||||
clippy::same_name_method,
|
||||
reason = "Warns on code generated by leptos macros"
|
||||
)]
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
#[expect(
|
||||
clippy::print_stdout,
|
||||
reason = "This is just simple and handy for such a small server"
|
||||
)]
|
||||
#[cfg(feature = "ssr")]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@ -5,7 +9,7 @@ async fn main() {
|
||||
use clap::Parser;
|
||||
use explorer_service::App;
|
||||
use leptos::prelude::*;
|
||||
use leptos_axum::{LeptosRoutes, generate_route_list};
|
||||
use leptos_axum::{LeptosRoutes as _, generate_route_list};
|
||||
use leptos_meta::MetaTags;
|
||||
|
||||
/// LEZ Block Explorer Server CLI arguments.
|
||||
|
||||
@ -10,11 +10,11 @@ use crate::{api, components::TransactionPreview};
|
||||
#[component]
|
||||
pub fn AccountPage() -> impl IntoView {
|
||||
let params = use_params_map();
|
||||
let (tx_offset, set_tx_offset) = signal(0u64);
|
||||
let (tx_offset, set_tx_offset) = signal(0_u64);
|
||||
let (all_transactions, set_all_transactions) = signal(Vec::new());
|
||||
let (is_loading, set_is_loading) = signal(false);
|
||||
let (has_more, set_has_more) = signal(true);
|
||||
let tx_limit = 10u64;
|
||||
let tx_limit = 10_u64;
|
||||
|
||||
// Parse account ID from URL params
|
||||
let account_id = move || {
|
||||
@ -27,7 +27,7 @@ pub fn AccountPage() -> impl IntoView {
|
||||
match acc_id_opt {
|
||||
Some(acc_id) => api::get_account(acc_id).await,
|
||||
None => Err(leptos::prelude::ServerFnError::ServerError(
|
||||
"Invalid account ID".to_string(),
|
||||
"Invalid account ID".to_owned(),
|
||||
)),
|
||||
}
|
||||
});
|
||||
@ -37,7 +37,7 @@ pub fn AccountPage() -> impl IntoView {
|
||||
match acc_id_opt {
|
||||
Some(acc_id) => api::get_transactions_by_account(acc_id, 0, tx_limit).await,
|
||||
None => Err(leptos::prelude::ServerFnError::ServerError(
|
||||
"Invalid account ID".to_string(),
|
||||
"Invalid account ID".to_owned(),
|
||||
)),
|
||||
}
|
||||
});
|
||||
@ -59,7 +59,7 @@ pub fn AccountPage() -> impl IntoView {
|
||||
};
|
||||
|
||||
set_is_loading.set(true);
|
||||
let current_offset = tx_offset.get() + tx_limit;
|
||||
let current_offset = tx_offset.get().saturating_add(tx_limit);
|
||||
set_tx_offset.set(current_offset);
|
||||
|
||||
leptos::task::spawn_local(async move {
|
||||
@ -111,101 +111,99 @@ pub fn AccountPage() -> impl IntoView {
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Account ID:"</span>
|
||||
<span class="info-value hash">{account_id_str}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Balance:"</span>
|
||||
<span class="info-value">{balance_str}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Program Owner:"</span>
|
||||
<span class="info-value hash">{program_id}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Nonce:"</span>
|
||||
<span class="info-value">{nonce_str}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Data:"</span>
|
||||
<span class="info-value">{format!("{data_len} bytes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Balance:"</span>
|
||||
<span class="info-value">{balance_str}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Program Owner:"</span>
|
||||
<span class="info-value hash">{program_id}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Nonce:"</span>
|
||||
<span class="info-value">{nonce_str}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Data:"</span>
|
||||
<span class="info-value">{format!("{data_len} bytes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="account-transactions">
|
||||
<h2>"Transactions"</h2>
|
||||
<Suspense fallback=move || {
|
||||
view! { <div class="loading">"Loading transactions..."</div> }
|
||||
}>
|
||||
|
||||
{move || {
|
||||
transactions_resource
|
||||
.get()
|
||||
.map(|result| match result {
|
||||
Ok(_) => {
|
||||
let txs = all_transactions.get();
|
||||
if txs.is_empty() {
|
||||
view! {
|
||||
<div class="no-transactions">
|
||||
"No transactions found"
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
} else {
|
||||
view! {
|
||||
<div>
|
||||
<div class="transactions-list">
|
||||
{txs
|
||||
.into_iter()
|
||||
.map(|tx| {
|
||||
view! { <TransactionPreview transaction=tx /> }
|
||||
})
|
||||
.collect::<Vec<_>>()}
|
||||
</div>
|
||||
{move || {
|
||||
if has_more.get() {
|
||||
view! {
|
||||
<button
|
||||
class="load-more-button"
|
||||
on:click=load_more
|
||||
disabled=move || is_loading.get()
|
||||
>
|
||||
{move || {
|
||||
if is_loading.get() {
|
||||
"Loading..."
|
||||
} else {
|
||||
"Load More"
|
||||
}
|
||||
}}
|
||||
|
||||
</button>
|
||||
}
|
||||
.into_any()
|
||||
} else {
|
||||
().into_any()
|
||||
<div class="account-transactions">
|
||||
<h2>"Transactions"</h2>
|
||||
<Suspense fallback=move || {
|
||||
view! { <div class="loading">"Loading transactions..."</div> }
|
||||
}>
|
||||
{move || {
|
||||
transactions_resource
|
||||
.get()
|
||||
.map(|load_tx_result| match load_tx_result {
|
||||
Ok(_) => {
|
||||
let txs = all_transactions.get();
|
||||
if txs.is_empty() {
|
||||
view! {
|
||||
<div class="no-transactions">
|
||||
"No transactions found"
|
||||
</div>
|
||||
}
|
||||
}}
|
||||
.into_any()
|
||||
} else {
|
||||
view! {
|
||||
<div>
|
||||
<div class="transactions-list">
|
||||
{txs
|
||||
.into_iter()
|
||||
.map(|tx| {
|
||||
view! { <TransactionPreview transaction=tx /> }
|
||||
})
|
||||
.collect::<Vec<_>>()}
|
||||
</div>
|
||||
{move || {
|
||||
if has_more.get() {
|
||||
view! {
|
||||
<button
|
||||
class="load-more-button"
|
||||
on:click=load_more
|
||||
disabled=move || is_loading.get()
|
||||
>
|
||||
{move || {
|
||||
if is_loading.get() {
|
||||
"Loading..."
|
||||
} else {
|
||||
"Load More"
|
||||
}
|
||||
}}
|
||||
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
view! {
|
||||
<div class="error">
|
||||
{format!("Failed to load transactions: {e}")}
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
})
|
||||
}}
|
||||
</button>
|
||||
}
|
||||
.into_any()
|
||||
} else {
|
||||
().into_any()
|
||||
}
|
||||
}}
|
||||
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
view! {
|
||||
<div class="error">
|
||||
{format!("Failed to load transactions: {e}")}
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
})
|
||||
}}
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
Err(e) => {
|
||||
view! {
|
||||
@ -218,7 +216,6 @@ pub fn AccountPage() -> impl IntoView {
|
||||
}
|
||||
})
|
||||
}}
|
||||
|
||||
</Suspense>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ pub fn BlockPage() -> impl IntoView {
|
||||
Some(BlockIdOrHash::BlockId(id)) => api::get_block_by_id(id).await,
|
||||
Some(BlockIdOrHash::Hash(hash)) => api::get_block_by_hash(hash).await,
|
||||
None => Err(leptos::prelude::ServerFnError::ServerError(
|
||||
"Invalid block ID or hash".to_string(),
|
||||
"Invalid block ID or hash".to_owned(),
|
||||
)),
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
pub mod account_page;
|
||||
pub mod block_page;
|
||||
pub mod main_page;
|
||||
pub mod transaction_page;
|
||||
|
||||
pub use account_page::AccountPage;
|
||||
pub use block_page::BlockPage;
|
||||
pub use main_page::MainPage;
|
||||
pub use transaction_page::TransactionPage;
|
||||
|
||||
pub mod account_page;
|
||||
pub mod block_page;
|
||||
pub mod main_page;
|
||||
pub mod transaction_page;
|
||||
|
||||
@ -4,7 +4,7 @@ use indexer_service_protocol::{
|
||||
HashType, PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage,
|
||||
ProgramDeploymentTransaction, PublicMessage, PublicTransaction, Transaction, WitnessSet,
|
||||
};
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
use itertools::{EitherOrBoth, Itertools as _};
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::{components::A, hooks::use_params_map};
|
||||
|
||||
@ -17,16 +17,14 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
|
||||
let transaction_resource = Resource::new(
|
||||
move || {
|
||||
params
|
||||
.read()
|
||||
.get("hash")
|
||||
.and_then(|s| HashType::from_str(&s).ok())
|
||||
let s = params.read().get("hash")?;
|
||||
HashType::from_str(&s).ok()
|
||||
},
|
||||
|hash_opt| async move {
|
||||
match hash_opt {
|
||||
Some(hash) => api::get_transaction(hash).await,
|
||||
None => Err(leptos::prelude::ServerFnError::ServerError(
|
||||
"Invalid transaction hash".to_string(),
|
||||
"Invalid transaction hash".to_owned(),
|
||||
)),
|
||||
}
|
||||
},
|
||||
@ -141,7 +139,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<span class="hash">{account_id_str}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Not affected by this transaction".to_string()}" )"
|
||||
" (nonce: "{"Not affected by this transaction".to_owned()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -153,7 +151,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<span class="hash">{"Account not found"}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Account not found".to_string()}" )"
|
||||
" (nonce: "{"Account not found".to_owned()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -244,7 +242,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<span class="hash">{account_id_str}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Not affected by this transaction".to_string()}" )"
|
||||
" (nonce: "{"Not affected by this transaction".to_owned()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -256,7 +254,7 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<span class="hash">{"Account not found"}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Account not found".to_string()}" )"
|
||||
" (nonce: "{"Account not found".to_owned()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -21,20 +21,16 @@ impl IndexerStore {
|
||||
/// ATTENTION: Will overwrite genesis block.
|
||||
pub fn open_db_with_genesis(
|
||||
location: &Path,
|
||||
start_data: Option<(Block, V02State)>,
|
||||
genesis_block: &Block,
|
||||
initial_state: &V02State,
|
||||
) -> Result<Self> {
|
||||
let dbio = RocksDBIO::open_or_create(location, start_data)?;
|
||||
let dbio = RocksDBIO::open_or_create(location, genesis_block, initial_state)?;
|
||||
|
||||
Ok(Self {
|
||||
dbio: Arc::new(dbio),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reopening existing database
|
||||
pub fn open_db_restart(location: &Path) -> Result<Self> {
|
||||
Self::open_db_with_genesis(location, None)
|
||||
}
|
||||
|
||||
pub fn last_observed_l1_lib_header(&self) -> Result<Option<HeaderId>> {
|
||||
Ok(self
|
||||
.dbio
|
||||
@ -120,6 +116,6 @@ impl IndexerStore {
|
||||
// to represent correct block finality
|
||||
block.bedrock_status = BedrockStatus::Finalized;
|
||||
|
||||
Ok(self.dbio.put_block(block, l1_header.into())?)
|
||||
Ok(self.dbio.put_block(&block, l1_header.into())?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ impl IndexerCore {
|
||||
// ToDo: remove key from indexer config, use some default.
|
||||
let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap();
|
||||
let channel_genesis_msg_id = [0; 32];
|
||||
let start_block = hashable_data.into_pending_block(&signing_key, channel_genesis_msg_id);
|
||||
let genesis_block = hashable_data.into_pending_block(&signing_key, channel_genesis_msg_id);
|
||||
|
||||
// This is a troubling moment, because changes in key protocol can
|
||||
// affect this. And indexer can not reliably ask this data from sequencer
|
||||
@ -94,7 +94,7 @@ impl IndexerCore {
|
||||
config.bedrock_client_config.auth.clone(),
|
||||
)?,
|
||||
config,
|
||||
store: IndexerStore::open_db_with_genesis(&home, Some((start_block, state)))?,
|
||||
store: IndexerStore::open_db_with_genesis(&home, &genesis_block, &state)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -102,9 +102,9 @@ impl IndexerCore {
|
||||
async_stream::stream! {
|
||||
info!("Searching for initial header");
|
||||
|
||||
let last_l1_lib_header = self.store.last_observed_l1_lib_header()?;
|
||||
let last_stored_l1_lib_header = self.store.last_observed_l1_lib_header()?;
|
||||
|
||||
let mut prev_last_l1_lib_header = if let Some(last_l1_lib_header) = last_l1_lib_header {
|
||||
let mut prev_last_l1_lib_header = if let Some(last_l1_lib_header) = last_stored_l1_lib_header {
|
||||
info!("Last l1 lib header found: {last_l1_lib_header}");
|
||||
last_l1_lib_header
|
||||
} else {
|
||||
@ -318,6 +318,10 @@ fn parse_block_owned(
|
||||
decoded_channel_id: &ChannelId,
|
||||
) -> (Vec<Block>, HeaderId) {
|
||||
(
|
||||
#[expect(
|
||||
clippy::wildcard_enum_match_arm,
|
||||
reason = "We are only interested in channel inscription ops, so it's fine to ignore the rest"
|
||||
)]
|
||||
l1_block
|
||||
.transactions()
|
||||
.flat_map(|tx| {
|
||||
|
||||
@ -14,6 +14,40 @@ use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
#[cfg(feature = "convert")]
|
||||
mod convert;
|
||||
|
||||
mod base64 {
|
||||
use base64::prelude::{BASE64_STANDARD, Engine as _};
|
||||
use serde::{Deserialize as _, Deserializer, Serialize as _, Serializer};
|
||||
|
||||
pub mod arr {
|
||||
use super::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
|
||||
super::serialize(v, s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, const N: usize, D: Deserializer<'de>>(
|
||||
d: D,
|
||||
) -> Result<[u8; N], D::Error> {
|
||||
let vec = super::deserialize(d)?;
|
||||
vec.try_into().map_err(|_bytes| {
|
||||
serde::de::Error::custom(format!("Invalid length, expected {N} bytes"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
|
||||
let base64 = BASE64_STANDARD.encode(v);
|
||||
String::serialize(&base64, s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
|
||||
let base64 = String::deserialize(d)?;
|
||||
BASE64_STANDARD
|
||||
.decode(base64.as_bytes())
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Nonce = u128;
|
||||
|
||||
#[derive(
|
||||
@ -23,24 +57,41 @@ pub struct ProgramId(pub [u32; 8]);
|
||||
|
||||
impl Display for ProgramId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let bytes: Vec<u8> = self.0.iter().flat_map(|n| n.to_be_bytes()).collect();
|
||||
let bytes: Vec<u8> = self.0.iter().flat_map(|n| n.to_le_bytes()).collect();
|
||||
write!(f, "{}", bytes.to_base58())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProgramIdParseError {
|
||||
InvalidBase58(base58::FromBase58Error),
|
||||
InvalidLength(usize),
|
||||
}
|
||||
|
||||
impl Display for ProgramIdParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ProgramIdParseError::InvalidBase58(err) => write!(f, "invalid base58: {err:?}"),
|
||||
ProgramIdParseError::InvalidLength(len) => {
|
||||
write!(f, "invalid length: expected 32 bytes, got {len}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ProgramId {
|
||||
type Err = hex::FromHexError;
|
||||
type Err = ProgramIdParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = s
|
||||
.from_base58()
|
||||
.map_err(|_| hex::FromHexError::InvalidStringLength)?;
|
||||
.map_err(ProgramIdParseError::InvalidBase58)?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(hex::FromHexError::InvalidStringLength);
|
||||
return Err(ProgramIdParseError::InvalidLength(bytes.len()));
|
||||
}
|
||||
let mut arr = [0u32; 8];
|
||||
let mut arr = [0_u32; 8];
|
||||
for (i, chunk) in bytes.chunks_exact(4).enumerate() {
|
||||
arr[i] = u32::from_be_bytes(chunk.try_into().unwrap());
|
||||
arr[i] = u32::from_le_bytes(chunk.try_into().unwrap());
|
||||
}
|
||||
Ok(ProgramId(arr))
|
||||
}
|
||||
@ -72,7 +123,7 @@ impl FromStr for AccountId {
|
||||
bytes.len()
|
||||
));
|
||||
}
|
||||
let mut value = [0u8; 32];
|
||||
let mut value = [0_u8; 32];
|
||||
value.copy_from_slice(&bytes);
|
||||
Ok(AccountId { value })
|
||||
}
|
||||
@ -121,7 +172,7 @@ impl FromStr for Signature {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut bytes = [0u8; 64];
|
||||
let mut bytes = [0_u8; 64];
|
||||
hex::decode_to_slice(s, &mut bytes)?;
|
||||
Ok(Signature(bytes))
|
||||
}
|
||||
@ -141,6 +192,7 @@ pub enum Transaction {
|
||||
|
||||
impl Transaction {
|
||||
/// Get the hash of the transaction
|
||||
#[expect(clippy::same_name_method, reason = "This is handy")]
|
||||
#[must_use]
|
||||
pub fn hash(&self) -> &self::HashType {
|
||||
match self {
|
||||
@ -284,7 +336,7 @@ impl FromStr for HashType {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut bytes = [0u8; 32];
|
||||
let mut bytes = [0_u8; 32];
|
||||
hex::decode_to_slice(s, &mut bytes)?;
|
||||
Ok(HashType(bytes))
|
||||
}
|
||||
@ -303,37 +355,3 @@ pub enum BedrockStatus {
|
||||
Safe,
|
||||
Finalized,
|
||||
}
|
||||
|
||||
mod base64 {
|
||||
use base64::prelude::{BASE64_STANDARD, Engine as _};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub mod arr {
|
||||
use super::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
|
||||
super::serialize(v, s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, const N: usize, D: Deserializer<'de>>(
|
||||
d: D,
|
||||
) -> Result<[u8; N], D::Error> {
|
||||
let vec = super::deserialize(d)?;
|
||||
vec.try_into().map_err(|_| {
|
||||
serde::de::Error::custom(format!("Invalid length, expected {N} bytes"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &[u8], s: S) -> Result<S::Ok, S::Error> {
|
||||
let base64 = BASE64_STANDARD.encode(v);
|
||||
String::serialize(&base64, s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
|
||||
let base64 = String::deserialize(d)?;
|
||||
BASE64_STANDARD
|
||||
.decode(base64.as_bytes())
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,10 @@ struct Args {
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[expect(
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "Generated by select! macro, can't be easily rewritten to avoid this lint"
|
||||
)]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#![expect(
|
||||
clippy::as_conversions,
|
||||
clippy::arithmetic_side_effects,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_lossless,
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "Mock service uses intentional casts and format patterns for test data generation"
|
||||
)]
|
||||
use std::collections::HashMap;
|
||||
@ -31,7 +34,7 @@ impl MockIndexerService {
|
||||
// Create some mock accounts
|
||||
let account_ids: Vec<AccountId> = (0..5)
|
||||
.map(|i| {
|
||||
let mut value = [0u8; 32];
|
||||
let mut value = [0_u8; 32];
|
||||
value[0] = i;
|
||||
AccountId { value }
|
||||
})
|
||||
@ -50,11 +53,11 @@ impl MockIndexerService {
|
||||
}
|
||||
|
||||
// Create 100 blocks with transactions
|
||||
let mut prev_hash = HashType([0u8; 32]);
|
||||
let mut prev_hash = HashType([0_u8; 32]);
|
||||
|
||||
for block_id in 1..=100 {
|
||||
let block_hash = {
|
||||
let mut hash = [0u8; 32];
|
||||
let mut hash = [0_u8; 32];
|
||||
hash[0] = block_id as u8;
|
||||
hash[1] = 0xff;
|
||||
HashType(hash)
|
||||
@ -67,7 +70,7 @@ impl MockIndexerService {
|
||||
|
||||
for tx_idx in 0..num_txs {
|
||||
let tx_hash = {
|
||||
let mut hash = [0u8; 32];
|
||||
let mut hash = [0_u8; 32];
|
||||
hash[0] = block_id as u8;
|
||||
hash[1] = tx_idx as u8;
|
||||
HashType(hash)
|
||||
@ -79,7 +82,7 @@ impl MockIndexerService {
|
||||
0 | 1 => Transaction::Public(PublicTransaction {
|
||||
hash: tx_hash,
|
||||
message: PublicMessage {
|
||||
program_id: ProgramId([1u32; 8]),
|
||||
program_id: ProgramId([1_u32; 8]),
|
||||
account_ids: vec![
|
||||
account_ids[tx_idx as usize % account_ids.len()],
|
||||
account_ids[(tx_idx as usize + 1) % account_ids.len()],
|
||||
@ -101,7 +104,7 @@ impl MockIndexerService {
|
||||
],
|
||||
nonces: vec![block_id as u128],
|
||||
public_post_states: vec![Account {
|
||||
program_owner: ProgramId([1u32; 8]),
|
||||
program_owner: ProgramId([1_u32; 8]),
|
||||
balance: 500,
|
||||
data: Data(vec![0xdd, 0xee]),
|
||||
nonce: block_id as u128,
|
||||
@ -143,7 +146,7 @@ impl MockIndexerService {
|
||||
prev_block_hash: prev_hash,
|
||||
hash: block_hash,
|
||||
timestamp: 1_704_067_200_000 + (block_id * 12_000), // ~12 seconds per block
|
||||
signature: Signature([0u8; 64]),
|
||||
signature: Signature([0_u8; 64]),
|
||||
},
|
||||
body: BlockBody {
|
||||
transactions: block_transactions,
|
||||
@ -191,7 +194,7 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
|
||||
.last()
|
||||
.map(|bl| bl.header.block_id)
|
||||
.ok_or_else(|| {
|
||||
ErrorObjectOwned::owned(-32001, "Last block not found".to_string(), None::<()>)
|
||||
ErrorObjectOwned::owned(-32001, "Last block not found".to_owned(), None::<()>)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -154,8 +154,10 @@ impl SubscriptionService {
|
||||
|
||||
pub async fn add_subscription(&self, subscription: Subscription<BlockId>) -> Result<()> {
|
||||
let guard = self.parts.load();
|
||||
if let Err(err) = guard.new_subscription_sender.send(subscription) {
|
||||
error!("Failed to send new subscription to subscription service with error: {err:#?}");
|
||||
if let Err(send_err) = guard.new_subscription_sender.send(subscription) {
|
||||
error!(
|
||||
"Failed to send new subscription to subscription service with error: {send_err:#?}"
|
||||
);
|
||||
|
||||
// Respawn the subscription service loop if it has finished (either with error or panic)
|
||||
if guard.handle.is_finished() {
|
||||
@ -177,7 +179,7 @@ impl SubscriptionService {
|
||||
}
|
||||
}
|
||||
|
||||
bail!(err)
|
||||
bail!(send_err)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -192,6 +194,10 @@ impl SubscriptionService {
|
||||
|
||||
let mut block_stream = pin!(indexer.subscribe_parse_block_stream());
|
||||
|
||||
#[expect(
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "Generated by select! macro, can't be easily rewritten to avoid this lint"
|
||||
)]
|
||||
loop {
|
||||
tokio::select! {
|
||||
sub = sub_receiver.recv() => {
|
||||
@ -289,7 +295,7 @@ pub fn not_yet_implemented_error() -> ErrorObjectOwned {
|
||||
fn db_error(err: anyhow::Error) -> ErrorObjectOwned {
|
||||
ErrorObjectOwned::owned(
|
||||
ErrorCode::InternalError.code(),
|
||||
"DBError".to_string(),
|
||||
"DBError".to_owned(),
|
||||
Some(format!("{err:#?}")),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::{net::SocketAddr, path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context as _, Result};
|
||||
use bytesize::ByteSize;
|
||||
use common::block::{AccountInitialData, CommitmentsInitialData};
|
||||
use indexer_service::{BackoffConfig, ChannelId, ClientConfig, IndexerConfig};
|
||||
@ -13,30 +13,6 @@ use wallet::config::{
|
||||
InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic, WalletConfig,
|
||||
};
|
||||
|
||||
pub fn indexer_config(
|
||||
bedrock_addr: SocketAddr,
|
||||
home: PathBuf,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<IndexerConfig> {
|
||||
Ok(IndexerConfig {
|
||||
home,
|
||||
consensus_info_polling_interval: Duration::from_secs(1),
|
||||
bedrock_client_config: ClientConfig {
|
||||
addr: addr_to_url(UrlProtocol::Http, bedrock_addr)
|
||||
.context("Failed to convert bedrock addr to URL")?,
|
||||
auth: None,
|
||||
backoff: BackoffConfig {
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 10,
|
||||
},
|
||||
},
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
channel_id: bedrock_channel_id(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sequencer config options available for custom changes in integration tests.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SequencerPartialConfig {
|
||||
@ -57,66 +33,6 @@ impl Default for SequencerPartialConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sequencer_config(
|
||||
partial: SequencerPartialConfig,
|
||||
home: PathBuf,
|
||||
bedrock_addr: SocketAddr,
|
||||
indexer_addr: SocketAddr,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<SequencerConfig> {
|
||||
let SequencerPartialConfig {
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout,
|
||||
} = partial;
|
||||
|
||||
Ok(SequencerConfig {
|
||||
home,
|
||||
override_rust_log: None,
|
||||
genesis_id: 1,
|
||||
is_genesis_random: true,
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(120),
|
||||
port: 0,
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
bedrock_config: BedrockConfig {
|
||||
backoff: BackoffConfig {
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
},
|
||||
channel_id: bedrock_channel_id(),
|
||||
node_url: addr_to_url(UrlProtocol::Http, bedrock_addr)
|
||||
.context("Failed to convert bedrock addr to URL")?,
|
||||
auth: None,
|
||||
},
|
||||
indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr)
|
||||
.context("Failed to convert indexer addr to URL")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn wallet_config(
|
||||
sequencer_addr: SocketAddr,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<WalletConfig> {
|
||||
Ok(WalletConfig {
|
||||
override_rust_log: None,
|
||||
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
||||
.context("Failed to convert sequencer addr to URL")?,
|
||||
seq_poll_timeout: Duration::from_secs(30),
|
||||
seq_tx_poll_max_blocks: 15,
|
||||
seq_poll_max_retries: 10,
|
||||
seq_block_poll_max_amount: 100,
|
||||
initial_accounts: initial_data.wallet_initial_accounts(),
|
||||
basic_auth: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct InitialData {
|
||||
pub public_accounts: Vec<(PrivateKey, u128)>,
|
||||
pub private_accounts: Vec<(KeyChain, Account)>,
|
||||
@ -223,11 +139,11 @@ impl InitialData {
|
||||
})
|
||||
.chain(self.private_accounts.iter().map(|(key_chain, account)| {
|
||||
let account_id = AccountId::from(&key_chain.nullifer_public_key);
|
||||
InitialAccountData::Private(InitialAccountDataPrivate {
|
||||
InitialAccountData::Private(Box::new(InitialAccountDataPrivate {
|
||||
account_id,
|
||||
account: account.clone(),
|
||||
key_chain: key_chain.clone(),
|
||||
})
|
||||
}))
|
||||
}))
|
||||
.collect()
|
||||
}
|
||||
@ -248,6 +164,90 @@ impl std::fmt::Display for UrlProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indexer_config(
|
||||
bedrock_addr: SocketAddr,
|
||||
home: PathBuf,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<IndexerConfig> {
|
||||
Ok(IndexerConfig {
|
||||
home,
|
||||
consensus_info_polling_interval: Duration::from_secs(1),
|
||||
bedrock_client_config: ClientConfig {
|
||||
addr: addr_to_url(UrlProtocol::Http, bedrock_addr)
|
||||
.context("Failed to convert bedrock addr to URL")?,
|
||||
auth: None,
|
||||
backoff: BackoffConfig {
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 10,
|
||||
},
|
||||
},
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
channel_id: bedrock_channel_id(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sequencer_config(
|
||||
partial: SequencerPartialConfig,
|
||||
home: PathBuf,
|
||||
bedrock_addr: SocketAddr,
|
||||
indexer_addr: SocketAddr,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<SequencerConfig> {
|
||||
let SequencerPartialConfig {
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout,
|
||||
} = partial;
|
||||
|
||||
Ok(SequencerConfig {
|
||||
home,
|
||||
override_rust_log: None,
|
||||
genesis_id: 1,
|
||||
is_genesis_random: true,
|
||||
max_num_tx_in_block,
|
||||
max_block_size,
|
||||
mempool_max_size,
|
||||
block_create_timeout,
|
||||
retry_pending_blocks_timeout: Duration::from_secs(120),
|
||||
port: 0,
|
||||
initial_accounts: initial_data.sequencer_initial_accounts(),
|
||||
initial_commitments: initial_data.sequencer_initial_commitments(),
|
||||
signing_key: [37; 32],
|
||||
bedrock_config: BedrockConfig {
|
||||
backoff: BackoffConfig {
|
||||
start_delay: Duration::from_millis(100),
|
||||
max_retries: 5,
|
||||
},
|
||||
channel_id: bedrock_channel_id(),
|
||||
node_url: addr_to_url(UrlProtocol::Http, bedrock_addr)
|
||||
.context("Failed to convert bedrock addr to URL")?,
|
||||
auth: None,
|
||||
},
|
||||
indexer_rpc_url: addr_to_url(UrlProtocol::Ws, indexer_addr)
|
||||
.context("Failed to convert indexer addr to URL")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn wallet_config(
|
||||
sequencer_addr: SocketAddr,
|
||||
initial_data: &InitialData,
|
||||
) -> Result<WalletConfig> {
|
||||
Ok(WalletConfig {
|
||||
override_rust_log: None,
|
||||
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
|
||||
.context("Failed to convert sequencer addr to URL")?,
|
||||
seq_poll_timeout: Duration::from_secs(30),
|
||||
seq_tx_poll_max_blocks: 15,
|
||||
seq_poll_max_retries: 10,
|
||||
seq_block_poll_max_amount: 100,
|
||||
initial_accounts: initial_data.wallet_initial_accounts(),
|
||||
basic_auth: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result<Url> {
|
||||
// Convert 0.0.0.0 to 127.0.0.1 for client connections
|
||||
// When binding to port 0, the server binds to 0.0.0.0:<random_port>
|
||||
@ -262,7 +262,7 @@ pub fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result<Url> {
|
||||
}
|
||||
|
||||
fn bedrock_channel_id() -> ChannelId {
|
||||
let channel_id: [u8; 32] = [0u8, 1]
|
||||
let channel_id: [u8; 32] = [0_u8, 1]
|
||||
.repeat(16)
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| unreachable!());
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
|
||||
use std::{net::SocketAddr, path::PathBuf, sync::LazyLock};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use common::{HashType, sequencer_client::SequencerClient, transaction::NSSATransaction};
|
||||
use futures::FutureExt as _;
|
||||
use indexer_service::IndexerHandle;
|
||||
use log::{debug, error, warn};
|
||||
use nssa::{AccountId, PrivacyPreservingTransaction};
|
||||
use nssa_core::Commitment;
|
||||
use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait};
|
||||
use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _};
|
||||
use sequencer_runner::SequencerHandle;
|
||||
use tempfile::TempDir;
|
||||
use testcontainers::compose::DockerCompose;
|
||||
@ -156,10 +156,12 @@ impl TestContext {
|
||||
}
|
||||
|
||||
let mut port = None;
|
||||
let mut attempt = 0;
|
||||
let max_attempts = 5;
|
||||
let mut attempt = 0_u32;
|
||||
let max_attempts = 5_u32;
|
||||
while port.is_none() && attempt < max_attempts {
|
||||
attempt += 1;
|
||||
attempt = attempt
|
||||
.checked_add(1)
|
||||
.expect("We check that attempt < max_attempts, so this won't overflow");
|
||||
match up_and_retrieve_port(&mut compose).await {
|
||||
Ok(p) => {
|
||||
port = Some(p);
|
||||
@ -449,6 +451,10 @@ pub fn format_private_account_id(account_id: AccountId) -> String {
|
||||
format!("Private/{account_id}")
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::wildcard_enum_match_arm,
|
||||
reason = "We want the code to panic if the transaction type is not PrivacyPreserving"
|
||||
)]
|
||||
pub async fn fetch_privacy_preserving_tx(
|
||||
seq_client: &SequencerClient,
|
||||
tx_hash: HashType,
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
#![expect(
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use anyhow::Result;
|
||||
use integration_tests::TestContext;
|
||||
use log::info;
|
||||
@ -36,7 +41,7 @@ async fn get_existing_account() -> Result<()> {
|
||||
async fn new_public_account_with_label() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let label = "my-test-public-account".to_string();
|
||||
let label = "my-test-public-account".to_owned();
|
||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||
cci: None,
|
||||
label: Some(label.clone()),
|
||||
@ -68,7 +73,7 @@ async fn new_public_account_with_label() -> Result<()> {
|
||||
async fn new_private_account_with_label() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let label = "my-test-private-account".to_string();
|
||||
let label = "my-test-private-account".to_owned();
|
||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||
cci: None,
|
||||
label: Some(label.clone()),
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
@ -108,7 +114,7 @@ async fn amm_public() -> Result<()> {
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id_1),
|
||||
supply_account_id: format_public_account_id(supply_account_id_1),
|
||||
name: "A NAM1".to_string(),
|
||||
name: "A NAM1".to_owned(),
|
||||
total_supply: 37,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@ -132,7 +138,7 @@ async fn amm_public() -> Result<()> {
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id_2),
|
||||
supply_account_id: format_public_account_id(supply_account_id_2),
|
||||
name: "A NAM2".to_string(),
|
||||
name: "A NAM2".to_owned(),
|
||||
total_supply: 37,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
|
||||
@ -1,2 +1,8 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
mod private;
|
||||
mod public;
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
#![expect(
|
||||
clippy::as_conversions,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
@ -24,7 +30,7 @@ async fn reject_oversized_transaction() -> Result<()> {
|
||||
// Create a transaction that's definitely too large
|
||||
// Block size is 1 MiB (1,048,576 bytes), minus ~200 bytes for header = ~1,048,376 bytes max tx
|
||||
// Create a 1.1 MiB binary to ensure it exceeds the limit
|
||||
let oversized_binary = vec![0u8; 1100 * 1024]; // 1.1 MiB binary
|
||||
let oversized_binary = vec![0_u8; 1100 * 1024]; // 1.1 MiB binary
|
||||
|
||||
let message = nssa::program_deployment_transaction::Message::new(oversized_binary);
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
@ -62,7 +68,7 @@ async fn accept_transaction_within_limit() -> Result<()> {
|
||||
.await?;
|
||||
|
||||
// Create a small program deployment that should fit
|
||||
let small_binary = vec![0u8; 1024]; // 1 KiB binary
|
||||
let small_binary = vec![0_u8; 1024]; // 1 KiB binary
|
||||
|
||||
let message = nssa::program_deployment_transaction::Message::new(small_binary);
|
||||
let tx = nssa::ProgramDeploymentTransaction::new(message);
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use anyhow::Result;
|
||||
use integration_tests::TestContext;
|
||||
use log::info;
|
||||
@ -12,8 +18,8 @@ async fn modify_config_field() -> Result<()> {
|
||||
|
||||
// Change config field
|
||||
let command = Command::Config(ConfigSubcommand::Set {
|
||||
key: "seq_poll_timeout".to_string(),
|
||||
value: "1s".to_string(),
|
||||
key: "seq_poll_timeout".to_owned(),
|
||||
value: "1s".to_owned(),
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
@ -22,7 +28,7 @@ async fn modify_config_field() -> Result<()> {
|
||||
|
||||
// Return how it was at the beginning
|
||||
let command = Command::Config(ConfigSubcommand::Set {
|
||||
key: "seq_poll_timeout".to_string(),
|
||||
key: "seq_poll_timeout".to_owned(),
|
||||
value: format!("{old_seq_poll_timeout:?}"),
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use indexer_service_rpc::RpcClient;
|
||||
use anyhow::{Context as _, Result};
|
||||
use indexer_service_rpc::RpcClient as _;
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id,
|
||||
format_public_account_id, verify_commitment_is_in_state,
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
use std::{str::FromStr, time::Duration};
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use std::{str::FromStr as _, time::Duration};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx,
|
||||
format_private_account_id, format_public_account_id, verify_commitment_is_in_state,
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
#![expect(
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
@ -69,7 +75,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create new token
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id),
|
||||
@ -317,7 +323,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create new token
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id),
|
||||
@ -475,7 +481,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with private definition
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(definition_account_id),
|
||||
@ -671,7 +677,7 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with both private definition and supply
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(definition_account_id),
|
||||
@ -843,7 +849,7 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id),
|
||||
@ -966,7 +972,7 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with private supply
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(definition_account_id),
|
||||
@ -1073,7 +1079,7 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token
|
||||
let name = "A NAME".to_string();
|
||||
let name = "A NAME".to_owned();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(definition_account_id),
|
||||
|
||||
@ -1,3 +1,14 @@
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
clippy::float_arithmetic,
|
||||
clippy::missing_asserts_for_indexing,
|
||||
clippy::as_conversions,
|
||||
clippy::tests_outside_test_module,
|
||||
clippy::integer_division,
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
@ -21,6 +32,102 @@ use nssa_core::{
|
||||
};
|
||||
use tokio::test;
|
||||
|
||||
pub(crate) struct TpsTestManager {
|
||||
public_keypairs: Vec<(PrivateKey, AccountId)>,
|
||||
target_tps: u64,
|
||||
}
|
||||
|
||||
impl TpsTestManager {
|
||||
/// Generates public account keypairs. These are used to populate the config and to generate
|
||||
/// valid public transactions for the tps test.
|
||||
pub(crate) fn new(target_tps: u64, number_transactions: usize) -> Self {
|
||||
let public_keypairs = (1..(number_transactions + 2))
|
||||
.map(|i| {
|
||||
let mut private_key_bytes = [0_u8; 32];
|
||||
private_key_bytes[..8].copy_from_slice(&i.to_le_bytes());
|
||||
let private_key = PrivateKey::try_new(private_key_bytes).unwrap();
|
||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||
let account_id = AccountId::from(&public_key);
|
||||
(private_key, account_id)
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
public_keypairs,
|
||||
target_tps,
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::cast_precision_loss,
|
||||
reason = "This is just for testing purposes, we don't care about precision loss here"
|
||||
)]
|
||||
pub(crate) fn target_time(&self) -> Duration {
|
||||
let number_transactions = (self.public_keypairs.len() - 1) as u64;
|
||||
Duration::from_secs_f64(number_transactions as f64 / self.target_tps as f64)
|
||||
}
|
||||
|
||||
/// Build a batch of public transactions to submit to the node.
|
||||
pub fn build_public_txs(&self) -> Vec<PublicTransaction> {
|
||||
// Create valid public transactions
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let public_txs: Vec<PublicTransaction> = self
|
||||
.public_keypairs
|
||||
.windows(2)
|
||||
.map(|pair| {
|
||||
let amount: u128 = 1;
|
||||
let message = putx::Message::try_new(
|
||||
program.id(),
|
||||
[pair[0].1, pair[1].1].to_vec(),
|
||||
[0_u128].to_vec(),
|
||||
amount,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set =
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[&pair[0].0]);
|
||||
PublicTransaction::new(message, witness_set)
|
||||
})
|
||||
.collect();
|
||||
|
||||
public_txs
|
||||
}
|
||||
|
||||
/// Generates a sequencer configuration with initial balance in a number of public accounts.
|
||||
/// The transactions generated with the function `build_public_txs` will be valid in a node
|
||||
/// started with the config from this method.
|
||||
fn generate_initial_data(&self) -> InitialData {
|
||||
// Create public public keypairs
|
||||
let public_accounts = self
|
||||
.public_keypairs
|
||||
.iter()
|
||||
.map(|(key, _)| (key.clone(), 10))
|
||||
.collect();
|
||||
|
||||
// Generate an initial commitment to be used with the privacy preserving transaction
|
||||
// created with the `build_privacy_transaction` function.
|
||||
let key_chain = KeyChain::new_os_random();
|
||||
let account = Account {
|
||||
balance: 100,
|
||||
nonce: 0xdead_beef,
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
data: Data::default(),
|
||||
};
|
||||
|
||||
InitialData {
|
||||
public_accounts,
|
||||
private_accounts: vec![(key_chain, account)],
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_sequencer_partial_config() -> SequencerPartialConfig {
|
||||
SequencerPartialConfig {
|
||||
max_num_tx_in_block: 300,
|
||||
max_block_size: ByteSize::mb(500),
|
||||
mempool_max_size: 10_000,
|
||||
block_create_timeout: Duration::from_secs(12),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make a proper benchmark instead of an ad-hoc test
|
||||
#[test]
|
||||
pub async fn tps_test() -> Result<()> {
|
||||
@ -95,102 +202,6 @@ pub async fn tps_test() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) struct TpsTestManager {
|
||||
public_keypairs: Vec<(PrivateKey, AccountId)>,
|
||||
target_tps: u64,
|
||||
}
|
||||
|
||||
impl TpsTestManager {
|
||||
/// Generates public account keypairs. These are used to populate the config and to generate
|
||||
/// valid public transactions for the tps test.
|
||||
pub(crate) fn new(target_tps: u64, number_transactions: usize) -> Self {
|
||||
let public_keypairs = (1..(number_transactions + 2))
|
||||
.map(|i| {
|
||||
let mut private_key_bytes = [0u8; 32];
|
||||
private_key_bytes[..8].copy_from_slice(&i.to_le_bytes());
|
||||
let private_key = PrivateKey::try_new(private_key_bytes).unwrap();
|
||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||
let account_id = AccountId::from(&public_key);
|
||||
(private_key, account_id)
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
public_keypairs,
|
||||
target_tps,
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::cast_precision_loss,
|
||||
reason = "This is just for testing purposes, we don't care about precision loss here"
|
||||
)]
|
||||
pub(crate) fn target_time(&self) -> Duration {
|
||||
let number_transactions = (self.public_keypairs.len() - 1) as u64;
|
||||
Duration::from_secs_f64(number_transactions as f64 / self.target_tps as f64)
|
||||
}
|
||||
|
||||
/// Build a batch of public transactions to submit to the node.
|
||||
pub fn build_public_txs(&self) -> Vec<PublicTransaction> {
|
||||
// Create valid public transactions
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let public_txs: Vec<PublicTransaction> = self
|
||||
.public_keypairs
|
||||
.windows(2)
|
||||
.map(|pair| {
|
||||
let amount: u128 = 1;
|
||||
let message = putx::Message::try_new(
|
||||
program.id(),
|
||||
[pair[0].1, pair[1].1].to_vec(),
|
||||
[0u128].to_vec(),
|
||||
amount,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set =
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[&pair[0].0]);
|
||||
PublicTransaction::new(message, witness_set)
|
||||
})
|
||||
.collect();
|
||||
|
||||
public_txs
|
||||
}
|
||||
|
||||
/// Generates a sequencer configuration with initial balance in a number of public accounts.
|
||||
/// The transactions generated with the function `build_public_txs` will be valid in a node
|
||||
/// started with the config from this method.
|
||||
fn generate_initial_data(&self) -> InitialData {
|
||||
// Create public public keypairs
|
||||
let public_accounts = self
|
||||
.public_keypairs
|
||||
.iter()
|
||||
.map(|(key, _)| (key.clone(), 10))
|
||||
.collect();
|
||||
|
||||
// Generate an initial commitment to be used with the privacy preserving transaction
|
||||
// created with the `build_privacy_transaction` function.
|
||||
let key_chain = KeyChain::new_os_random();
|
||||
let account = Account {
|
||||
balance: 100,
|
||||
nonce: 0xdead_beef,
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
data: Data::default(),
|
||||
};
|
||||
|
||||
InitialData {
|
||||
public_accounts,
|
||||
private_accounts: vec![(key_chain, account)],
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_sequencer_partial_config() -> SequencerPartialConfig {
|
||||
SequencerPartialConfig {
|
||||
max_num_tx_in_block: 300,
|
||||
max_block_size: ByteSize::mb(500),
|
||||
mempool_max_size: 10_000,
|
||||
block_create_timeout: Duration::from_secs(12),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a single privacy transaction to use in stress tests. This involves generating a proof so
|
||||
/// it may take a while to run. In normal execution of the node this transaction will be accepted
|
||||
/// only once. Disabling the node's nullifier uniqueness check allows to submit this transaction
|
||||
|
||||
@ -1,7 +1,19 @@
|
||||
#![expect(
|
||||
clippy::redundant_test_prefix,
|
||||
reason = "Otherwise names interfere with ffi bindings"
|
||||
)]
|
||||
#![expect(
|
||||
clippy::tests_outside_test_module,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
clippy::multiple_unsafe_ops_per_block,
|
||||
clippy::shadow_unrelated,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ffi::{CStr, CString, c_char},
|
||||
io::Write,
|
||||
io::Write as _,
|
||||
path::Path,
|
||||
time::Duration,
|
||||
};
|
||||
@ -208,7 +220,7 @@ fn new_wallet_rust_with_default_config(password: &str) -> Result<WalletCore> {
|
||||
config_path.clone(),
|
||||
storage_path.clone(),
|
||||
None,
|
||||
password.to_string(),
|
||||
password.to_owned(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -222,7 +234,7 @@ fn load_existing_ffi_wallet(home: &Path) -> Result<*mut WalletHandle> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_create_public_accounts() -> Result<()> {
|
||||
fn wallet_ffi_create_public_accounts() -> Result<()> {
|
||||
let password = "password_for_tests";
|
||||
let n_accounts = 10;
|
||||
// First `n_accounts` public accounts created with Rust wallet
|
||||
@ -257,7 +269,7 @@ fn test_wallet_ffi_create_public_accounts() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_create_private_accounts() -> Result<()> {
|
||||
fn wallet_ffi_create_private_accounts() -> Result<()> {
|
||||
let password = "password_for_tests";
|
||||
let n_accounts = 10;
|
||||
// First `n_accounts` private accounts created with Rust wallet
|
||||
@ -291,7 +303,7 @@ fn test_wallet_ffi_create_private_accounts() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn test_wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
|
||||
fn wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let mut out_private_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
let home = tempfile::tempdir()?;
|
||||
@ -635,7 +647,7 @@ fn test_wallet_ffi_account_id_to_base58() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_base58_to_account_id() -> Result<()> {
|
||||
fn wallet_ffi_base58_to_account_id() -> Result<()> {
|
||||
let private_key = PrivateKey::new_os_random();
|
||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||
let account_id = AccountId::from(&public_key);
|
||||
@ -655,7 +667,7 @@ fn test_wallet_ffi_base58_to_account_id() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
fn wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
@ -717,7 +729,7 @@ fn test_wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
|
||||
fn wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
@ -791,7 +803,7 @@ fn test_wallet_ffi_transfer_public() -> Result<()> {
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
let from: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
||||
let to: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[1]).into();
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
@ -854,7 +866,7 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
|
||||
);
|
||||
(out_account_id, out_keys)
|
||||
};
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
@ -918,7 +930,7 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
||||
let to = FfiBytes32::from_bytes([37; 32]);
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
@ -989,7 +1001,7 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
|
||||
(out_account_id, out_keys)
|
||||
};
|
||||
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
|
||||
@ -2,8 +2,8 @@ use nssa_core::{
|
||||
NullifierPublicKey, SharedSecretKey,
|
||||
encryption::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey},
|
||||
};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use sha2::Digest;
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use sha2::Digest as _;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral
|
||||
@ -12,18 +12,6 @@ pub struct EphemeralKeyHolder {
|
||||
ephemeral_secret_key: EphemeralSecretKey,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn produce_one_sided_shared_secret_receiver(
|
||||
vpk: &ViewingPublicKey,
|
||||
) -> (SharedSecretKey, EphemeralPublicKey) {
|
||||
let mut esk = [0; 32];
|
||||
OsRng.fill_bytes(&mut esk);
|
||||
(
|
||||
SharedSecretKey::new(&esk, vpk),
|
||||
EphemeralPublicKey::from_scalar(esk),
|
||||
)
|
||||
}
|
||||
|
||||
impl EphemeralKeyHolder {
|
||||
#[must_use]
|
||||
pub fn new(receiver_nullifier_public_key: &NullifierPublicKey) -> Self {
|
||||
@ -51,3 +39,15 @@ impl EphemeralKeyHolder {
|
||||
SharedSecretKey::new(&self.ephemeral_secret_key, receiver_viewing_public_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn produce_one_sided_shared_secret_receiver(
|
||||
vpk: &ViewingPublicKey,
|
||||
) -> (SharedSecretKey, EphemeralPublicKey) {
|
||||
let mut esk = [0; 32];
|
||||
OsRng.fill_bytes(&mut esk);
|
||||
(
|
||||
SharedSecretKey::new(&esk, vpk),
|
||||
EphemeralPublicKey::from_scalar(esk),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use itertools::Itertools;
|
||||
use itertools::Itertools as _;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, Hash)]
|
||||
@ -76,14 +76,14 @@ impl ChainIndex {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn next_in_line(&self) -> ChainIndex {
|
||||
pub fn next_in_line(&self) -> Option<ChainIndex> {
|
||||
let mut chain = self.0.clone();
|
||||
// ToDo: Add overflow check
|
||||
if let Some(last_p) = chain.last_mut() {
|
||||
*last_p += 1;
|
||||
*last_p = last_p.checked_add(1)?;
|
||||
}
|
||||
|
||||
ChainIndex(chain)
|
||||
Some(ChainIndex(chain))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -101,7 +101,8 @@ impl ChainIndex {
|
||||
if self.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ChainIndex(self.0[..(self.0.len() - 1)].to_vec()))
|
||||
let last = self.0.len().checked_sub(1)?;
|
||||
Some(ChainIndex(self.0[..last].to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,14 +116,17 @@ impl ChainIndex {
|
||||
|
||||
#[must_use]
|
||||
pub fn depth(&self) -> u32 {
|
||||
self.0.iter().map(|cci| cci + 1).sum()
|
||||
self.0
|
||||
.iter()
|
||||
.map(|cci| cci.checked_add(1).expect("Max cci reached"))
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn collapse_back(&self) -> Option<Self> {
|
||||
let mut res = self.parent()?;
|
||||
|
||||
let last_mut = res.0.last_mut()?;
|
||||
*last_mut += *(self.0.last()?) + 1;
|
||||
*last_mut = last_mut.checked_add(self.0.last()?.checked_add(1)?)?;
|
||||
|
||||
Some(res)
|
||||
}
|
||||
@ -139,8 +143,8 @@ impl ChainIndex {
|
||||
let mut stack = vec![ChainIndex(vec![0; depth])];
|
||||
let mut cumulative_stack = vec![ChainIndex(vec![0; depth])];
|
||||
|
||||
while let Some(id) = stack.pop() {
|
||||
if let Some(collapsed_id) = id.collapse_back() {
|
||||
while let Some(top_id) = stack.pop() {
|
||||
if let Some(collapsed_id) = top_id.collapse_back() {
|
||||
for id in collapsed_id.shuffle_iter() {
|
||||
stack.push(id.clone());
|
||||
cumulative_stack.push(id);
|
||||
@ -155,8 +159,8 @@ impl ChainIndex {
|
||||
let mut stack = vec![ChainIndex(vec![0; depth])];
|
||||
let mut cumulative_stack = vec![ChainIndex(vec![0; depth])];
|
||||
|
||||
while let Some(id) = stack.pop() {
|
||||
if let Some(collapsed_id) = id.collapse_back() {
|
||||
while let Some(top_id) = stack.pop() {
|
||||
if let Some(collapsed_id) = top_id.collapse_back() {
|
||||
for id in collapsed_id.shuffle_iter() {
|
||||
stack.push(id.clone());
|
||||
cumulative_stack.push(id);
|
||||
@ -173,7 +177,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_root_correct() {
|
||||
fn chain_id_root_correct() {
|
||||
let chain_id = ChainIndex::root();
|
||||
let chain_id_2 = ChainIndex::from_str("/").unwrap();
|
||||
|
||||
@ -181,21 +185,21 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_deser_correct() {
|
||||
fn chain_id_deser_correct() {
|
||||
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||
|
||||
assert_eq!(chain_id.chain(), &[257]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_deser_failure_no_root() {
|
||||
fn chain_id_deser_failure_no_root() {
|
||||
let chain_index_error = ChainIndex::from_str("257").err().unwrap();
|
||||
|
||||
assert!(matches!(chain_index_error, ChainIndexError::NoRootFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_deser_failure_int_parsing_failure() {
|
||||
fn chain_id_deser_failure_int_parsing_failure() {
|
||||
let chain_index_error = ChainIndex::from_str("/hello").err().unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
@ -205,15 +209,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_next_in_line_correct() {
|
||||
fn chain_id_next_in_line_correct() {
|
||||
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||
let next_in_line = chain_id.next_in_line();
|
||||
let next_in_line = chain_id.next_in_line().unwrap();
|
||||
|
||||
assert_eq!(next_in_line, ChainIndex::from_str("/258").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_child_correct() {
|
||||
fn chain_id_child_correct() {
|
||||
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||
let child = chain_id.nth_child(3);
|
||||
|
||||
@ -221,16 +225,16 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_correct_display() {
|
||||
fn correct_display() {
|
||||
let chainid = ChainIndex(vec![5, 7, 8]);
|
||||
|
||||
let string_index = format!("{chainid}");
|
||||
|
||||
assert_eq!(string_index, "/5/7/8".to_string());
|
||||
assert_eq!(string_index, "/5/7/8".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prev_in_line() {
|
||||
fn prev_in_line() {
|
||||
let chain_id = ChainIndex(vec![1, 7, 3]);
|
||||
|
||||
let prev_chain_id = chain_id.previous_in_line().unwrap();
|
||||
@ -239,7 +243,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prev_in_line_no_prev() {
|
||||
fn prev_in_line_no_prev() {
|
||||
let chain_id = ChainIndex(vec![1, 7, 0]);
|
||||
|
||||
let prev_chain_id = chain_id.previous_in_line();
|
||||
@ -248,7 +252,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent() {
|
||||
fn parent() {
|
||||
let chain_id = ChainIndex(vec![1, 7, 3]);
|
||||
|
||||
let parent_chain_id = chain_id.parent().unwrap();
|
||||
@ -257,7 +261,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent_no_parent() {
|
||||
fn parent_no_parent() {
|
||||
let chain_id = ChainIndex(vec![]);
|
||||
|
||||
let parent_chain_id = chain_id.parent();
|
||||
@ -266,7 +270,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent_root() {
|
||||
fn parent_root() {
|
||||
let chain_id = ChainIndex(vec![1]);
|
||||
|
||||
let parent_chain_id = chain_id.parent().unwrap();
|
||||
@ -275,7 +279,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collapse_back() {
|
||||
fn collapse_back() {
|
||||
let chain_id = ChainIndex(vec![1, 1]);
|
||||
|
||||
let collapsed = chain_id.collapse_back().unwrap();
|
||||
@ -284,7 +288,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collapse_back_one() {
|
||||
fn collapse_back_one() {
|
||||
let chain_id = ChainIndex(vec![1]);
|
||||
|
||||
let collapsed = chain_id.collapse_back();
|
||||
@ -293,7 +297,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collapse_back_root() {
|
||||
fn collapse_back_root() {
|
||||
let chain_id = ChainIndex(vec![]);
|
||||
|
||||
let collapsed = chain_id.collapse_back();
|
||||
@ -302,7 +306,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shuffle() {
|
||||
fn shuffle() {
|
||||
for id in ChainIndex::chain_ids_at_depth(5) {
|
||||
println!("{id}");
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use k256::{Scalar, elliptic_curve::PrimeField};
|
||||
use k256::{Scalar, elliptic_curve::PrimeField as _};
|
||||
use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -54,6 +54,10 @@ impl KeyNode for ChildKeysPrivate {
|
||||
}
|
||||
|
||||
fn nth_child(&self, cci: u32) -> Self {
|
||||
#[expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "Multiplying finite field scalars gives no unexpected side effects"
|
||||
)]
|
||||
let parent_pt =
|
||||
Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
||||
.expect("Key generated as scalar, must be valid representation")
|
||||
@ -113,27 +117,27 @@ impl KeyNode for ChildKeysPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ChildKeysPrivate> for &'a (KeyChain, nssa::Account) {
|
||||
fn from(value: &'a ChildKeysPrivate) -> Self {
|
||||
impl<'keys> From<&'keys ChildKeysPrivate> for &'keys (KeyChain, nssa::Account) {
|
||||
fn from(value: &'keys ChildKeysPrivate) -> Self {
|
||||
&value.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut ChildKeysPrivate> for &'a mut (KeyChain, nssa::Account) {
|
||||
fn from(value: &'a mut ChildKeysPrivate) -> Self {
|
||||
impl<'keys> From<&'keys mut ChildKeysPrivate> for &'keys mut (KeyChain, nssa::Account) {
|
||||
fn from(value: &'keys mut ChildKeysPrivate) -> Self {
|
||||
&mut value.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nssa_core::{NullifierPublicKey, NullifierSecretKey};
|
||||
use nssa_core::NullifierSecretKey;
|
||||
|
||||
use super::*;
|
||||
use crate::key_management::{self, secret_holders::ViewingSecretKey};
|
||||
|
||||
#[test]
|
||||
fn test_master_key_generation() {
|
||||
fn master_key_generation() {
|
||||
let seed: [u8; 64] = [
|
||||
252, 56, 204, 83, 232, 123, 209, 188, 187, 167, 39, 213, 71, 39, 58, 65, 125, 134, 255,
|
||||
49, 43, 108, 92, 53, 173, 164, 94, 142, 150, 74, 21, 163, 43, 144, 226, 87, 199, 18,
|
||||
@ -143,7 +147,7 @@ mod tests {
|
||||
|
||||
let keys = ChildKeysPrivate::root(seed);
|
||||
|
||||
let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
246, 79, 26, 124, 135, 95, 52, 51, 201, 27, 48, 194, 2, 144, 51, 219, 245, 128, 139,
|
||||
222, 42, 195, 105, 33, 115, 97, 186, 0, 97, 14, 218, 191,
|
||||
]);
|
||||
@ -158,7 +162,7 @@ mod tests {
|
||||
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
|
||||
];
|
||||
|
||||
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
||||
let expected_npk = nssa_core::NullifierPublicKey([
|
||||
7, 123, 125, 191, 233, 183, 201, 4, 20, 214, 155, 210, 45, 234, 27, 240, 194, 111, 97,
|
||||
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
|
||||
]);
|
||||
@ -181,7 +185,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_child_keys_generation() {
|
||||
fn child_keys_generation() {
|
||||
let seed: [u8; 64] = [
|
||||
252, 56, 204, 83, 232, 123, 209, 188, 187, 167, 39, 213, 71, 39, 58, 65, 125, 134, 255,
|
||||
49, 43, 108, 92, 53, 173, 164, 94, 142, 150, 74, 21, 163, 43, 144, 226, 87, 199, 18,
|
||||
@ -190,7 +194,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let root_node = ChildKeysPrivate::root(seed);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42u32);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ccc: [u8; 32] = [
|
||||
145, 59, 225, 32, 54, 168, 14, 45, 60, 253, 57, 202, 31, 86, 142, 234, 51, 57, 154, 88,
|
||||
@ -201,7 +205,7 @@ mod tests {
|
||||
19, 100, 119, 73, 191, 225, 234, 219, 129, 88, 40, 229, 63, 225, 189, 136, 69, 172,
|
||||
221, 186, 147, 83, 150, 207, 70, 17, 228, 70, 113, 87, 227, 31,
|
||||
];
|
||||
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
||||
let expected_npk = nssa_core::NullifierPublicKey([
|
||||
133, 235, 223, 151, 12, 69, 26, 222, 60, 125, 235, 125, 167, 212, 201, 168, 101, 242,
|
||||
111, 239, 1, 228, 12, 252, 146, 53, 75, 17, 187, 255, 122, 181,
|
||||
]);
|
||||
|
||||
@ -16,7 +16,7 @@ impl ChildKeysPublic {
|
||||
fn compute_hash_value(&self, cci: u32) -> [u8; 64] {
|
||||
let mut hash_input = vec![];
|
||||
|
||||
if 2u32.pow(31) > cci {
|
||||
if 2_u32.pow(31) > cci {
|
||||
// Non-harden
|
||||
hash_input.extend_from_slice(self.cpk.value());
|
||||
hash_input.extend_from_slice(&cci.to_le_bytes());
|
||||
@ -97,8 +97,8 @@ impl KeyNode for ChildKeysPublic {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ChildKeysPublic> for &'a nssa::PrivateKey {
|
||||
fn from(value: &'a ChildKeysPublic) -> Self {
|
||||
impl<'keys> From<&'keys ChildKeysPublic> for &'keys nssa::PrivateKey {
|
||||
fn from(value: &'keys ChildKeysPublic) -> Self {
|
||||
&value.csk
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_master_keys_generation() {
|
||||
fn master_keys_generation() {
|
||||
let seed = [
|
||||
88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173,
|
||||
134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87,
|
||||
@ -141,7 +141,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_harden_child_keys_generation() {
|
||||
fn harden_child_keys_generation() {
|
||||
let seed = [
|
||||
88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173,
|
||||
134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87,
|
||||
@ -149,7 +149,7 @@ mod tests {
|
||||
187, 148, 92, 44, 253, 210, 37,
|
||||
];
|
||||
let root_keys = ChildKeysPublic::root(seed);
|
||||
let cci = (2u32).pow(31) + 13;
|
||||
let cci = (2_u32).pow(31) + 13;
|
||||
let child_keys = ChildKeysPublic::nth_child(&root_keys, cci);
|
||||
|
||||
print!(
|
||||
@ -181,7 +181,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonharden_child_keys_generation() {
|
||||
fn nonharden_child_keys_generation() {
|
||||
let seed = [
|
||||
88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173,
|
||||
134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87,
|
||||
@ -221,7 +221,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_case_child_keys_generation_2_power_31() {
|
||||
fn edge_case_child_keys_generation_2_power_31() {
|
||||
let seed = [
|
||||
88, 189, 37, 237, 199, 125, 151, 226, 69, 153, 165, 113, 191, 69, 188, 221, 9, 34, 173,
|
||||
134, 61, 109, 34, 103, 121, 39, 237, 14, 107, 194, 24, 194, 191, 14, 237, 185, 12, 87,
|
||||
@ -229,7 +229,7 @@ mod tests {
|
||||
187, 148, 92, 44, 253, 210, 37,
|
||||
];
|
||||
let root_keys = ChildKeysPublic::root(seed);
|
||||
let cci = (2u32).pow(31); //equivant to 0, thus non-harden.
|
||||
let cci = (2_u32).pow(31); //equivant to 0, thus non-harden.
|
||||
let child_keys = ChildKeysPublic::nth_child(&root_keys, cci);
|
||||
|
||||
let expected_ccc = [
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use common::sequencer_client::SequencerClient;
|
||||
@ -25,7 +22,7 @@ pub const DEPTH_SOFT_CAP: u32 = 20;
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct KeyTree<N: KeyNode> {
|
||||
pub key_map: BTreeMap<ChainIndex, N>,
|
||||
pub account_id_map: HashMap<nssa::AccountId, ChainIndex>,
|
||||
pub account_id_map: BTreeMap<nssa::AccountId, ChainIndex>,
|
||||
}
|
||||
|
||||
pub type KeyTreePublic = KeyTree<ChildKeysPublic>;
|
||||
@ -44,7 +41,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
let account_id = root_keys.account_id();
|
||||
|
||||
let key_map = BTreeMap::from_iter([(ChainIndex::root(), root_keys)]);
|
||||
let account_id_map = HashMap::from_iter([(account_id, ChainIndex::root())]);
|
||||
let account_id_map = BTreeMap::from_iter([(account_id, ChainIndex::root())]);
|
||||
|
||||
Self {
|
||||
key_map,
|
||||
@ -53,7 +50,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
}
|
||||
|
||||
pub fn new_from_root(root: N) -> Self {
|
||||
let account_id_map = HashMap::from_iter([(root.account_id(), ChainIndex::root())]);
|
||||
let account_id_map = BTreeMap::from_iter([(root.account_id(), ChainIndex::root())]);
|
||||
let key_map = BTreeMap::from_iter([(ChainIndex::root(), root)]);
|
||||
|
||||
Self {
|
||||
@ -84,7 +81,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
let rightmost_child = parent_id.nth_child(right);
|
||||
|
||||
let rightmost_ref = self.key_map.get(&rightmost_child);
|
||||
let rightmost_ref_next = self.key_map.get(&rightmost_child.next_in_line());
|
||||
let rightmost_ref_next = self.key_map.get(&rightmost_child.next_in_line()?);
|
||||
|
||||
match (&rightmost_ref, &rightmost_ref_next) {
|
||||
(Some(_), Some(_)) => {
|
||||
@ -92,7 +89,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
right = u32::midpoint(right, right_border);
|
||||
}
|
||||
(Some(_), None) => {
|
||||
break Some(right + 1);
|
||||
break Some(right.checked_add(1)?);
|
||||
}
|
||||
(None, None) => {
|
||||
right_border = right;
|
||||
@ -133,7 +130,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
break 'outer chain_id;
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
depth = depth.checked_add(1).expect("Max depth reached");
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,15 +153,13 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
|
||||
#[must_use]
|
||||
pub fn get_node(&self, account_id: nssa::AccountId) -> Option<&N> {
|
||||
self.account_id_map
|
||||
.get(&account_id)
|
||||
.and_then(|chain_id| self.key_map.get(chain_id))
|
||||
let chain_id = self.account_id_map.get(&account_id)?;
|
||||
self.key_map.get(chain_id)
|
||||
}
|
||||
|
||||
pub fn get_node_mut(&mut self, account_id: nssa::AccountId) -> Option<&mut N> {
|
||||
self.account_id_map
|
||||
.get(&account_id)
|
||||
.and_then(|chain_id| self.key_map.get_mut(chain_id))
|
||||
let chain_id = self.account_id_map.get(&account_id)?;
|
||||
self.key_map.get_mut(chain_id)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, account_id: nssa::AccountId, chain_index: ChainIndex, node: N) {
|
||||
@ -173,7 +168,7 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, addr: nssa::AccountId) -> Option<N> {
|
||||
let chain_index = self.account_id_map.remove(&addr).unwrap();
|
||||
let chain_index = self.account_id_map.remove(&addr)?;
|
||||
self.key_map.remove(&chain_index)
|
||||
}
|
||||
|
||||
@ -192,7 +187,10 @@ impl<N: KeyNode> KeyTree<N> {
|
||||
while (next_id.depth()) < depth {
|
||||
self.generate_new_node(&curr_id);
|
||||
id_stack.push(next_id.clone());
|
||||
next_id = next_id.next_in_line();
|
||||
next_id = match next_id.next_in_line() {
|
||||
Some(id) => id,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,7 +223,10 @@ impl KeyTree<ChildKeysPrivate> {
|
||||
|
||||
while (next_id.depth()) < depth {
|
||||
id_stack.push(next_id.clone());
|
||||
next_id = next_id.next_in_line();
|
||||
next_id = match next_id.next_in_line() {
|
||||
Some(id) => id,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,7 +241,8 @@ impl KeyTree<ChildKeysPrivate> {
|
||||
///
|
||||
/// Slow, maintains tree consistency.
|
||||
pub fn cleanup_tree_remove_uninit_layered(&mut self, depth: u32) {
|
||||
'outer: for i in (1..(depth as usize)).rev() {
|
||||
let depth = usize::try_from(depth).expect("Depth is expected to fit in usize");
|
||||
'outer: for i in (1..depth).rev() {
|
||||
println!("Cleanup of tree at depth {i}");
|
||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||
if let Some(node) = self.key_map.get(&id) {
|
||||
@ -286,7 +288,10 @@ impl KeyTree<ChildKeysPublic> {
|
||||
|
||||
while (next_id.depth()) < depth {
|
||||
id_stack.push(next_id.clone());
|
||||
next_id = next_id.next_in_line();
|
||||
next_id = match next_id.next_in_line() {
|
||||
Some(id) => id,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +310,8 @@ impl KeyTree<ChildKeysPublic> {
|
||||
depth: u32,
|
||||
client: Arc<SequencerClient>,
|
||||
) -> Result<()> {
|
||||
'outer: for i in (1..(depth as usize)).rev() {
|
||||
let depth = usize::try_from(depth).expect("Depth is expected to fit in usize");
|
||||
'outer: for i in (1..depth).rev() {
|
||||
println!("Cleanup of tree at depth {i}");
|
||||
for id in ChainIndex::chain_ids_at_depth(i) {
|
||||
if let Some(node) = self.key_map.get(&id) {
|
||||
@ -328,7 +334,9 @@ impl KeyTree<ChildKeysPublic> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about this in tests")]
|
||||
|
||||
use std::{collections::HashSet, str::FromStr as _};
|
||||
|
||||
use nssa::AccountId;
|
||||
|
||||
@ -341,7 +349,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_key_tree() {
|
||||
fn simple_key_tree() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let tree = KeyTreePublic::new(&seed_holder);
|
||||
@ -354,7 +362,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_small_key_tree() {
|
||||
fn small_key_tree() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let mut tree = KeyTreePrivate::new(&seed_holder);
|
||||
@ -393,7 +401,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_tree_can_not_make_child_keys() {
|
||||
fn key_tree_can_not_make_child_keys() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let mut tree = KeyTreePrivate::new(&seed_holder);
|
||||
@ -423,7 +431,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_tree_complex_structure() {
|
||||
fn key_tree_complex_structure() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let mut tree = KeyTreePublic::new(&seed_holder);
|
||||
@ -518,7 +526,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree_balancing_automatic() {
|
||||
fn tree_balancing_automatic() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let mut tree = KeyTreePublic::new(&seed_holder);
|
||||
@ -533,7 +541,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup() {
|
||||
fn cleanup() {
|
||||
let seed_holder = seed_holder_for_tests();
|
||||
|
||||
let mut tree = KeyTreePrivate::new(&seed_holder);
|
||||
@ -566,13 +574,13 @@ mod tests {
|
||||
tree.cleanup_tree_remove_uninit_layered(10);
|
||||
|
||||
let mut key_set_res = HashSet::new();
|
||||
key_set_res.insert("/0".to_string());
|
||||
key_set_res.insert("/1".to_string());
|
||||
key_set_res.insert("/2".to_string());
|
||||
key_set_res.insert("/".to_string());
|
||||
key_set_res.insert("/0/0".to_string());
|
||||
key_set_res.insert("/0/1".to_string());
|
||||
key_set_res.insert("/1/0".to_string());
|
||||
key_set_res.insert("/0".to_owned());
|
||||
key_set_res.insert("/1".to_owned());
|
||||
key_set_res.insert("/2".to_owned());
|
||||
key_set_res.insert("/".to_owned());
|
||||
key_set_res.insert("/0/0".to_owned());
|
||||
key_set_res.insert("/0/1".to_owned());
|
||||
key_set_res.insert("/1/0".to_owned());
|
||||
|
||||
let mut key_set = HashSet::new();
|
||||
|
||||
@ -582,28 +590,16 @@ mod tests {
|
||||
|
||||
assert_eq!(key_set, key_set_res);
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get(&ChainIndex::from_str("/1").unwrap())
|
||||
.unwrap();
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/1").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 2);
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get(&ChainIndex::from_str("/2").unwrap())
|
||||
.unwrap();
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/2").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 3);
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get(&ChainIndex::from_str("/0/1").unwrap())
|
||||
.unwrap();
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/0/1").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 5);
|
||||
|
||||
let acc = tree
|
||||
.key_map
|
||||
.get(&ChainIndex::from_str("/1/0").unwrap())
|
||||
.unwrap();
|
||||
let acc = &tree.key_map[&ChainIndex::from_str("/1/0").unwrap()];
|
||||
assert_eq!(acc.value.1.balance, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@ use nssa_core::{
|
||||
use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub type PublicAccountSigningKey = [u8; 32];
|
||||
|
||||
pub mod ephemeral_key_holder;
|
||||
pub mod key_tree;
|
||||
pub mod secret_holders;
|
||||
|
||||
pub type PublicAccountSigningKey = [u8; 32];
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
/// Entrypoint to key management
|
||||
pub struct KeyChain {
|
||||
@ -77,9 +77,9 @@ impl KeyChain {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use aes_gcm::aead::OsRng;
|
||||
use base58::ToBase58;
|
||||
use k256::{AffinePoint, elliptic_curve::group::GroupEncoding};
|
||||
use rand::RngCore;
|
||||
use base58::ToBase58 as _;
|
||||
use k256::{AffinePoint, elliptic_curve::group::GroupEncoding as _};
|
||||
use rand::RngCore as _;
|
||||
|
||||
use super::*;
|
||||
use crate::key_management::{
|
||||
@ -87,19 +87,19 @@ mod tests {
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_new_os_random() {
|
||||
fn new_os_random() {
|
||||
// Ensure that a new KeyChain instance can be created without errors.
|
||||
let account_id_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Check that key holder fields are initialized with expected types
|
||||
assert_ne!(
|
||||
account_id_key_holder.nullifer_public_key.as_ref(),
|
||||
&[0u8; 32]
|
||||
&[0_u8; 32]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_shared_secret_receiver() {
|
||||
fn calculate_shared_secret_receiver() {
|
||||
let account_id_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate a random ephemeral public key sender
|
||||
@ -180,7 +180,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_trivial_chain_index() {
|
||||
fn non_trivial_chain_index() {
|
||||
let keys = account_with_chain_index_2_for_tests();
|
||||
|
||||
let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifer_public_key);
|
||||
|
||||
@ -4,9 +4,9 @@ use nssa_core::{
|
||||
NullifierPublicKey, NullifierSecretKey,
|
||||
encryption::{Scalar, ViewingPublicKey},
|
||||
};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
const NSSA_ENTROPY_BYTES: [u8; 32] = [0; 32];
|
||||
|
||||
@ -27,6 +27,7 @@ pub type ViewingSecretKey = Scalar;
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
||||
/// for recepient.
|
||||
#[expect(clippy::partial_pub_fields, reason = "TODO: fix later")]
|
||||
pub struct PrivateKeyHolder {
|
||||
pub nullifier_secret_key: NullifierSecretKey,
|
||||
pub(crate) viewing_secret_key: ViewingSecretKey,
|
||||
@ -84,7 +85,7 @@ impl SecretSpendingKey {
|
||||
const SUFFIX_2: &[u8; 19] = &[0; 19];
|
||||
|
||||
let index = match index {
|
||||
None => 0u32,
|
||||
None => 0_u32,
|
||||
_ => index.expect("Expect a valid u32"),
|
||||
};
|
||||
|
||||
@ -105,7 +106,7 @@ impl SecretSpendingKey {
|
||||
const SUFFIX_2: &[u8; 19] = &[0; 19];
|
||||
|
||||
let index = match index {
|
||||
None => 0u32,
|
||||
None => 0_u32,
|
||||
_ => index.expect("Expect a valid u32"),
|
||||
};
|
||||
|
||||
@ -158,7 +159,7 @@ mod tests {
|
||||
|
||||
assert_eq!(seed_holder.seed.len(), 64);
|
||||
|
||||
let _ = seed_holder.generate_secret_spending_key_hash();
|
||||
let _hash = seed_holder.generate_secret_spending_key_hash();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -169,15 +170,15 @@ mod tests {
|
||||
|
||||
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
let _ = top_secret_key_holder.generate_viewing_secret_key(None);
|
||||
let _vsk = top_secret_key_holder.generate_viewing_secret_key(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_seeds_generated_same_from_same_mnemonic() {
|
||||
let mnemonic = "test_pass";
|
||||
|
||||
let seed_holder1 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
||||
let seed_holder2 = SeedHolder::new_mnemonic(mnemonic.to_string());
|
||||
let seed_holder1 = SeedHolder::new_mnemonic(mnemonic.to_owned());
|
||||
let seed_holder2 = SeedHolder::new_mnemonic(mnemonic.to_owned());
|
||||
|
||||
assert_eq!(seed_holder1.seed, seed_holder2.seed);
|
||||
}
|
||||
|
||||
@ -190,8 +190,8 @@ impl Default for NSSAUserData {
|
||||
Self::new_with_accounts(
|
||||
BTreeMap::new(),
|
||||
BTreeMap::new(),
|
||||
KeyTreePublic::new(&SeedHolder::new_mnemonic("default".to_string())),
|
||||
KeyTreePrivate::new(&SeedHolder::new_mnemonic("default".to_string())),
|
||||
KeyTreePublic::new(&SeedHolder::new_mnemonic("default".to_owned())),
|
||||
KeyTreePrivate::new(&SeedHolder::new_mnemonic("default".to_owned())),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
@ -202,7 +202,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new_account() {
|
||||
fn new_account() {
|
||||
let mut user_data = NSSAUserData::default();
|
||||
|
||||
let (account_id_private, _) = user_data
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
#![expect(clippy::print_stdout, reason = "TODO: fix later")]
|
||||
|
||||
pub mod key_management;
|
||||
pub mod key_protocol_core;
|
||||
|
||||
@ -66,13 +66,13 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
async fn test_mempool_new() {
|
||||
async fn mempool_new() {
|
||||
let (mut pool, _handle): (MemPool<u64>, _) = MemPool::new(10);
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_push_and_pop() {
|
||||
async fn push_and_pop() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
@ -83,7 +83,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_multiple_push_pop() {
|
||||
async fn multiple_push_pop() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
@ -97,13 +97,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_pop_empty() {
|
||||
async fn pop_empty() {
|
||||
let (mut pool, _handle): (MemPool<u64>, _) = MemPool::new(10);
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_max_size() {
|
||||
async fn max_size() {
|
||||
let (mut pool, handle) = MemPool::new(2);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
@ -116,7 +116,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_push_front() {
|
||||
async fn push_front() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
|
||||
@ -10,6 +10,7 @@ workspace = true
|
||||
[dependencies]
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
risc0-zkvm.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@ -3,7 +3,7 @@ use std::{
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use base58::{FromBase58, ToBase58};
|
||||
use base58::{FromBase58 as _, ToBase58 as _};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
pub use data::Data;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -125,7 +125,7 @@ impl FromStr for AccountId {
|
||||
if bytes.len() != 32 {
|
||||
return Err(AccountIdError::InvalidLength(bytes.len()));
|
||||
}
|
||||
let mut value = [0u8; 32];
|
||||
let mut value = [0_u8; 32];
|
||||
value.copy_from_slice(&bytes);
|
||||
Ok(AccountId { value })
|
||||
}
|
||||
@ -143,28 +143,28 @@ mod tests {
|
||||
use crate::program::DEFAULT_PROGRAM_ID;
|
||||
|
||||
#[test]
|
||||
fn test_zero_balance_account_data_creation() {
|
||||
fn zero_balance_account_data_creation() {
|
||||
let new_acc = Account::default();
|
||||
|
||||
assert_eq!(new_acc.balance, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_nonce_account_data_creation() {
|
||||
fn zero_nonce_account_data_creation() {
|
||||
let new_acc = Account::default();
|
||||
|
||||
assert_eq!(new_acc.nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_data_account_data_creation() {
|
||||
fn empty_data_account_data_creation() {
|
||||
let new_acc = Account::default();
|
||||
|
||||
assert!(new_acc.data.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_program_owner_account_data_creation() {
|
||||
fn default_program_owner_account_data_creation() {
|
||||
let new_acc = Account::default();
|
||||
|
||||
assert_eq!(new_acc.program_owner, DEFAULT_PROGRAM_ID);
|
||||
@ -172,7 +172,7 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn test_account_with_metadata_constructor() {
|
||||
fn account_with_metadata_constructor() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
@ -194,7 +194,7 @@ mod tests {
|
||||
fn parse_valid_account_id() {
|
||||
let base58_str = "11111111111111111111111111111111";
|
||||
let account_id: AccountId = base58_str.parse().unwrap();
|
||||
assert_eq!(account_id.value, [0u8; 32]);
|
||||
assert_eq!(account_id.value, [0_u8; 32]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
|
||||
@ -22,18 +22,17 @@ impl Data {
|
||||
) -> Result<Self, crate::error::NssaCoreError> {
|
||||
use std::io::Read as _;
|
||||
|
||||
let mut u32_bytes = [0u8; 4];
|
||||
let mut u32_bytes = [0_u8; 4];
|
||||
cursor.read_exact(&mut u32_bytes)?;
|
||||
let data_length = u32::from_le_bytes(u32_bytes);
|
||||
if data_length as usize
|
||||
> usize::try_from(DATA_MAX_LENGTH.as_u64()).expect("DATA_MAX_LENGTH fits in usize")
|
||||
{
|
||||
if u64::from(data_length) > DATA_MAX_LENGTH.as_u64() {
|
||||
return Err(
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, DataTooBigError).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut data = vec![0; data_length as usize];
|
||||
let mut data =
|
||||
vec![0; usize::try_from(data_length).expect("data length is expected to fit in usize")];
|
||||
cursor.read_exact(&mut data)?;
|
||||
Ok(Self(data))
|
||||
}
|
||||
@ -135,15 +134,10 @@ impl BorshDeserialize for Data {
|
||||
let len = u32::deserialize_reader(reader)?;
|
||||
match len {
|
||||
0 => Ok(Self::default()),
|
||||
len if len as usize
|
||||
> usize::try_from(DATA_MAX_LENGTH.as_u64())
|
||||
.expect("DATA_MAX_LENGTH fits in usize") =>
|
||||
{
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
DataTooBigError,
|
||||
))
|
||||
}
|
||||
len if u64::from(len) > DATA_MAX_LENGTH.as_u64() => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
DataTooBigError,
|
||||
)),
|
||||
len => {
|
||||
let vec_bytes = u8::vec_from_reader(len, reader)?
|
||||
.expect("can't be None in current borsh crate implementation");
|
||||
@ -158,9 +152,9 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_data_max_length_allowed() {
|
||||
fn data_max_length_allowed() {
|
||||
let max_vec = vec![
|
||||
0u8;
|
||||
0_u8;
|
||||
usize::try_from(DATA_MAX_LENGTH.as_u64())
|
||||
.expect("DATA_MAX_LENGTH fits in usize")
|
||||
];
|
||||
@ -169,9 +163,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_too_big_error() {
|
||||
fn data_too_big_error() {
|
||||
let big_vec = vec![
|
||||
0u8;
|
||||
0_u8;
|
||||
usize::try_from(DATA_MAX_LENGTH.as_u64())
|
||||
.expect("DATA_MAX_LENGTH fits in usize")
|
||||
+ 1
|
||||
@ -181,9 +175,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borsh_deserialize_exceeding_limit_error() {
|
||||
fn borsh_deserialize_exceeding_limit_error() {
|
||||
let too_big_data = vec![
|
||||
0u8;
|
||||
0_u8;
|
||||
usize::try_from(DATA_MAX_LENGTH.as_u64())
|
||||
.expect("DATA_MAX_LENGTH fits in usize")
|
||||
+ 1
|
||||
@ -196,9 +190,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_deserialize_exceeding_limit_error() {
|
||||
fn json_deserialize_exceeding_limit_error() {
|
||||
let data = vec![
|
||||
0u8;
|
||||
0_u8;
|
||||
usize::try_from(DATA_MAX_LENGTH.as_u64())
|
||||
.expect("DATA_MAX_LENGTH fits in usize")
|
||||
+ 1
|
||||
|
||||
@ -61,7 +61,7 @@ mod tests {
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() {
|
||||
fn privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() {
|
||||
let output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states: vec![
|
||||
AccountWithMetadata::new(
|
||||
@ -69,7 +69,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 12_345_678_901_234_567_890,
|
||||
data: b"test data".to_vec().try_into().unwrap(),
|
||||
nonce: 18_446_744_073_709_551_614,
|
||||
nonce: 0xFFFF_FFFF_FFFF_FFFE,
|
||||
},
|
||||
true,
|
||||
AccountId::new([0; 32]),
|
||||
@ -89,7 +89,7 @@ mod tests {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 100,
|
||||
data: b"post state data".to_vec().try_into().unwrap(),
|
||||
nonce: 18_446_744_073_709_551_615,
|
||||
nonce: 0xFFFF_FFFF_FFFF_FFFF,
|
||||
}],
|
||||
ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])],
|
||||
new_commitments: vec![Commitment::new(
|
||||
|
||||
@ -1,29 +1,9 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{NullifierPublicKey, account::Account};
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)
|
||||
)]
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
#[cfg(any(feature = "host", test))]
|
||||
impl std::fmt::Debug for Commitment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
||||
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
||||
acc
|
||||
});
|
||||
write!(f, "Commitment({hex})")
|
||||
}
|
||||
}
|
||||
|
||||
/// A commitment to all zero data.
|
||||
/// ```python
|
||||
/// from hashlib import sha256
|
||||
@ -48,6 +28,26 @@ pub const DUMMY_COMMITMENT_HASH: [u8; 32] = [
|
||||
194, 216, 67, 56, 251, 208, 226, 0, 117, 149, 39,
|
||||
];
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)
|
||||
)]
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
#[cfg(any(feature = "host", test))]
|
||||
impl std::fmt::Debug for Commitment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
||||
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
||||
acc
|
||||
});
|
||||
write!(f, "Commitment({hex})")
|
||||
}
|
||||
}
|
||||
|
||||
impl Commitment {
|
||||
/// Generates the commitment to a private account owned by user for npk:
|
||||
/// SHA256(npk || `program_owner` || balance || nonce || SHA256(data))
|
||||
@ -93,12 +93,12 @@ pub fn compute_digest_for_path(
|
||||
for node in &proof.1 {
|
||||
let is_left_child = level_index & 1 == 0;
|
||||
if is_left_child {
|
||||
let mut bytes = [0u8; 64];
|
||||
let mut bytes = [0_u8; 64];
|
||||
bytes[..32].copy_from_slice(&result);
|
||||
bytes[32..].copy_from_slice(node);
|
||||
result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
} else {
|
||||
let mut bytes = [0u8; 64];
|
||||
let mut bytes = [0_u8; 64];
|
||||
bytes[..32].copy_from_slice(node);
|
||||
bytes[32..].copy_from_slice(&result);
|
||||
result = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
@ -110,14 +110,14 @@ pub fn compute_digest_for_path(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
use crate::{
|
||||
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, NullifierPublicKey, account::Account,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_nothing_up_my_sleeve_dummy_commitment() {
|
||||
fn nothing_up_my_sleeve_dummy_commitment() {
|
||||
let default_account = Account::default();
|
||||
let npk_null = NullifierPublicKey([0; 32]);
|
||||
let expected_dummy_commitment = Commitment::new(&npk_null, &default_account);
|
||||
@ -125,7 +125,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nothing_up_my_sleeve_dummy_commitment_hash() {
|
||||
fn nothing_up_my_sleeve_dummy_commitment_hash() {
|
||||
let expected_dummy_commitment_hash: [u8; 32] =
|
||||
Impl::hash_bytes(&DUMMY_COMMITMENT.to_byte_array())
|
||||
.as_bytes()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#[cfg(feature = "host")]
|
||||
use std::io::Cursor;
|
||||
#[cfg(feature = "host")]
|
||||
use std::io::Read;
|
||||
use std::io::Read as _;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use crate::Nullifier;
|
||||
@ -37,11 +37,11 @@ impl Account {
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
use crate::account::data::Data;
|
||||
|
||||
let mut u32_bytes = [0u8; 4];
|
||||
let mut u128_bytes = [0u8; 16];
|
||||
let mut u32_bytes = [0_u8; 4];
|
||||
let mut u128_bytes = [0_u8; 16];
|
||||
|
||||
// program owner
|
||||
let mut program_owner = [0u32; 8];
|
||||
let mut program_owner = [0_u32; 8];
|
||||
for word in &mut program_owner {
|
||||
cursor.read_exact(&mut u32_bytes)?;
|
||||
*word = u32::from_le_bytes(u32_bytes);
|
||||
@ -82,7 +82,7 @@ impl Commitment {
|
||||
/// Deserializes a commitment from a cursor.
|
||||
#[cfg(feature = "host")]
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
let mut bytes = [0u8; 32];
|
||||
let mut bytes = [0_u8; 32];
|
||||
cursor.read_exact(&mut bytes)?;
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
@ -110,7 +110,7 @@ impl Nullifier {
|
||||
|
||||
/// Deserializes a nullifier from a cursor.
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
let mut bytes = [0u8; 32];
|
||||
let mut bytes = [0_u8; 32];
|
||||
cursor.read_exact(&mut bytes)?;
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
@ -148,7 +148,9 @@ impl Ciphertext {
|
||||
|
||||
cursor.read_exact(&mut u32_bytes)?;
|
||||
let ciphertext_lenght = u32::from_le_bytes(u32_bytes);
|
||||
let mut ciphertext = vec![0; ciphertext_lenght as usize];
|
||||
let ciphertext_length =
|
||||
usize::try_from(ciphertext_lenght).expect("ciphertext length fits in usize");
|
||||
let mut ciphertext = vec![0; ciphertext_length];
|
||||
cursor.read_exact(&mut ciphertext)?;
|
||||
|
||||
Ok(Self(ciphertext))
|
||||
@ -183,7 +185,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_enconding() {
|
||||
fn enconding() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 123_456_789_012_345_678_901_234_567_890_123_456,
|
||||
@ -204,7 +206,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_commitment_to_bytes() {
|
||||
fn commitment_to_bytes() {
|
||||
let commitment = Commitment((0..32).collect::<Vec<u8>>().try_into().unwrap());
|
||||
let expected_bytes: [u8; 32] = (0..32).collect::<Vec<u8>>().try_into().unwrap();
|
||||
|
||||
@ -214,7 +216,7 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn test_nullifier_to_bytes() {
|
||||
fn nullifier_to_bytes() {
|
||||
let nullifier = Nullifier((0..32).collect::<Vec<u8>>().try_into().unwrap());
|
||||
let expected_bytes: [u8; 32] = (0..32).collect::<Vec<u8>>().try_into().unwrap();
|
||||
|
||||
@ -224,7 +226,7 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn test_commitment_to_bytes_roundtrip() {
|
||||
fn commitment_to_bytes_roundtrip() {
|
||||
let commitment = Commitment((0..32).collect::<Vec<u8>>().try_into().unwrap());
|
||||
let bytes = commitment.to_byte_array();
|
||||
let mut cursor = Cursor::new(bytes.as_ref());
|
||||
@ -234,7 +236,7 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn test_nullifier_to_bytes_roundtrip() {
|
||||
fn nullifier_to_bytes_roundtrip() {
|
||||
let nullifier = Nullifier((0..32).collect::<Vec<u8>>().try_into().unwrap());
|
||||
let bytes = nullifier.to_byte_array();
|
||||
let mut cursor = Cursor::new(bytes.as_ref());
|
||||
@ -244,7 +246,7 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn test_account_to_bytes_roundtrip() {
|
||||
fn account_to_bytes_roundtrip() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 123_456_789_012_345_678_901_234_567_890_123_456,
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use chacha20::{
|
||||
ChaCha20,
|
||||
cipher::{KeyIvInit, StreamCipher},
|
||||
cipher::{KeyIvInit as _, StreamCipher as _},
|
||||
};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub mod shared_key_derivation;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey};
|
||||
|
||||
use crate::{Commitment, account::Account};
|
||||
#[cfg(feature = "host")]
|
||||
pub mod shared_key_derivation;
|
||||
|
||||
pub type Scalar = [u8; 32];
|
||||
|
||||
@ -78,6 +76,10 @@ impl EncryptionScheme {
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[expect(
|
||||
clippy::print_stdout,
|
||||
reason = "This is the current way to debug things. TODO: fix later"
|
||||
)]
|
||||
#[must_use]
|
||||
pub fn decrypt(
|
||||
ciphertext: &Ciphertext,
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "Multiplication of finite field elements can't overflow"
|
||||
)]
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use k256::{
|
||||
AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint,
|
||||
elliptic_curve::{
|
||||
PrimeField,
|
||||
sec1::{FromEncodedPoint, ToEncodedPoint},
|
||||
PrimeField as _,
|
||||
sec1::{FromEncodedPoint as _, ToEncodedPoint as _},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -61,9 +66,9 @@ impl SharedSecretKey {
|
||||
let shared = ProjectivePoint::from(pubkey_affine) * scalar;
|
||||
let shared_affine = shared.to_affine();
|
||||
|
||||
let encoded = shared_affine.to_encoded_point(false);
|
||||
let x_bytes_slice = encoded.x().unwrap();
|
||||
let mut x_bytes = [0u8; 32];
|
||||
let shared_affine_encoded = shared_affine.to_encoded_point(false);
|
||||
let x_bytes_slice = shared_affine_encoded.x().unwrap();
|
||||
let mut x_bytes = [0_u8; 32];
|
||||
x_bytes.copy_from_slice(x_bytes_slice);
|
||||
|
||||
Self(x_bytes)
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
pub mod account;
|
||||
mod circuit_io;
|
||||
mod commitment;
|
||||
mod encoding;
|
||||
pub mod encryption;
|
||||
mod nullifier;
|
||||
pub mod program;
|
||||
#![expect(
|
||||
clippy::multiple_inherent_impl,
|
||||
reason = "We prefer to group methods by functionality rather than by type for encoding"
|
||||
)]
|
||||
|
||||
pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput};
|
||||
pub use commitment::{
|
||||
@ -14,5 +11,13 @@ pub use commitment::{
|
||||
pub use encryption::{EncryptionScheme, SharedSecretKey};
|
||||
pub use nullifier::{Nullifier, NullifierPublicKey, NullifierSecretKey};
|
||||
|
||||
pub mod account;
|
||||
mod circuit_io;
|
||||
mod commitment;
|
||||
mod encoding;
|
||||
pub mod encryption;
|
||||
mod nullifier;
|
||||
pub mod program;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub mod error;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Commitment, account::AccountId};
|
||||
@ -88,8 +88,8 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_constructor_for_account_update() {
|
||||
let commitment = Commitment((0..32u8).collect::<Vec<_>>().try_into().unwrap());
|
||||
fn constructor_for_account_update() {
|
||||
let commitment = Commitment((0..32_u8).collect::<Vec<_>>().try_into().unwrap());
|
||||
let nsk = [0x42; 32];
|
||||
let expected_nullifier = Nullifier([
|
||||
148, 243, 116, 209, 140, 231, 211, 61, 35, 62, 114, 110, 143, 224, 82, 201, 221, 34,
|
||||
@ -100,7 +100,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_constructor_for_account_initialization() {
|
||||
fn constructor_for_account_initialization() {
|
||||
let npk = NullifierPublicKey([
|
||||
112, 188, 193, 129, 150, 55, 228, 67, 88, 168, 29, 151, 5, 92, 23, 190, 17, 162, 164,
|
||||
255, 29, 105, 42, 186, 43, 11, 157, 168, 132, 225, 17, 163,
|
||||
@ -114,7 +114,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_secret_key() {
|
||||
fn from_secret_key() {
|
||||
let nsk = [
|
||||
57, 5, 64, 115, 153, 56, 184, 51, 207, 238, 99, 165, 147, 214, 213, 151, 30, 251, 30,
|
||||
196, 134, 22, 224, 211, 237, 120, 136, 225, 188, 220, 249, 28,
|
||||
@ -128,7 +128,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_account_id_from_nullifier_public_key() {
|
||||
fn account_id_from_nullifier_public_key() {
|
||||
let nsk = [
|
||||
57, 5, 64, 115, 153, 56, 184, 51, 207, 238, 99, 165, 147, 214, 213, 151, 30, 251, 30,
|
||||
196, 134, 22, 224, 211, 237, 120, 136, 225, 188, 220, 249, 28,
|
||||
|
||||
@ -5,11 +5,11 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::account::{Account, AccountId, AccountWithMetadata};
|
||||
|
||||
pub type ProgramId = [u32; 8];
|
||||
pub type InstructionData = Vec<u32>;
|
||||
pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8];
|
||||
pub const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
||||
|
||||
pub type ProgramId = [u32; 8];
|
||||
pub type InstructionData = Vec<u32>;
|
||||
pub struct ProgramInput<T> {
|
||||
pub pre_states: Vec<AccountWithMetadata>,
|
||||
pub instruction: T,
|
||||
@ -30,24 +30,9 @@ impl PdaSeed {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_authorized_pdas(
|
||||
caller_program_id: Option<ProgramId>,
|
||||
pda_seeds: &[PdaSeed],
|
||||
) -> HashSet<AccountId> {
|
||||
caller_program_id
|
||||
.map(|caller_program_id| {
|
||||
pda_seeds
|
||||
.iter()
|
||||
.map(|pda_seed| AccountId::from((&caller_program_id, pda_seed)))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl From<(&ProgramId, &PdaSeed)> for AccountId {
|
||||
fn from(value: (&ProgramId, &PdaSeed)) -> Self {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
const PROGRAM_DERIVED_ACCOUNT_ID_PREFIX: &[u8; 32] =
|
||||
b"/NSSA/v0.2/AccountId/PDA/\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
@ -176,6 +161,48 @@ pub struct ProgramOutput {
|
||||
pub chained_calls: Vec<ChainedCall>,
|
||||
}
|
||||
|
||||
/// Representation of a number as `lo + hi * 2^128`.
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct WrappedBalanceSum {
|
||||
lo: u128,
|
||||
hi: u128,
|
||||
}
|
||||
|
||||
impl WrappedBalanceSum {
|
||||
/// Constructs a [`WrappedBalanceSum`] from an iterator of balances.
|
||||
///
|
||||
/// Returns [`None`] if balance sum overflows `lo + hi * 2^128` representation, which is not
|
||||
/// expected in practical scenarios.
|
||||
fn from_balances(balances: impl Iterator<Item = u128>) -> Option<Self> {
|
||||
let mut wrapped = WrappedBalanceSum { lo: 0, hi: 0 };
|
||||
|
||||
for balance in balances {
|
||||
let (new_sum, did_overflow) = wrapped.lo.overflowing_add(balance);
|
||||
if did_overflow {
|
||||
wrapped.hi = wrapped.hi.checked_add(1)?;
|
||||
}
|
||||
wrapped.lo = new_sum;
|
||||
}
|
||||
|
||||
Some(wrapped)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_authorized_pdas(
|
||||
caller_program_id: Option<ProgramId>,
|
||||
pda_seeds: &[PdaSeed],
|
||||
) -> HashSet<AccountId> {
|
||||
caller_program_id
|
||||
.map(|caller_program_id| {
|
||||
pda_seeds
|
||||
.iter()
|
||||
.map(|pda_seed| AccountId::from((&caller_program_id, pda_seed)))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Reads the NSSA inputs from the guest environment.
|
||||
#[must_use]
|
||||
pub fn read_nssa_inputs<T: DeserializeOwned>() -> (ProgramInput<T>, InstructionData) {
|
||||
@ -310,39 +337,12 @@ fn validate_uniqueness_of_account_ids(pre_states: &[AccountWithMetadata]) -> boo
|
||||
number_of_accounts == number_of_account_ids
|
||||
}
|
||||
|
||||
/// Representation of a number as `lo + hi * 2^128`.
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct WrappedBalanceSum {
|
||||
lo: u128,
|
||||
hi: u128,
|
||||
}
|
||||
|
||||
impl WrappedBalanceSum {
|
||||
/// Constructs a [`WrappedBalanceSum`] from an iterator of balances.
|
||||
///
|
||||
/// Returns [`None`] if balance sum overflows `lo + hi * 2^128` representation, which is not
|
||||
/// expected in practical scenarios.
|
||||
fn from_balances(balances: impl Iterator<Item = u128>) -> Option<Self> {
|
||||
let mut wrapped = WrappedBalanceSum { lo: 0, hi: 0 };
|
||||
|
||||
for balance in balances {
|
||||
let (new_sum, did_overflow) = wrapped.lo.overflowing_add(balance);
|
||||
if did_overflow {
|
||||
wrapped.hi = wrapped.hi.checked_add(1)?;
|
||||
}
|
||||
wrapped.lo = new_sum;
|
||||
}
|
||||
|
||||
Some(wrapped)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_post_state_new_with_claim_constructor() {
|
||||
fn post_state_new_with_claim_constructor() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
@ -357,7 +357,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_post_state_new_without_claim_constructor() {
|
||||
fn post_state_new_without_claim_constructor() {
|
||||
let account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
@ -372,7 +372,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_post_state_account_getter() {
|
||||
fn post_state_account_getter() {
|
||||
let mut account = Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
balance: 1337,
|
||||
|
||||
@ -16,7 +16,7 @@ mod tests {
|
||||
use crate::{ProgramDeploymentTransaction, program_deployment_transaction::Message};
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
fn roundtrip() {
|
||||
let message = Message::new(vec![0xca, 0xfe, 0xca, 0xfe, 0x01, 0x02, 0x03]);
|
||||
let tx = ProgramDeploymentTransaction::new(message);
|
||||
let bytes = tx.to_bytes();
|
||||
|
||||
@ -29,7 +29,7 @@ pub enum NssaError {
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error("Invalid Public Key")]
|
||||
InvalidPublicKey,
|
||||
InvalidPublicKey(#[source] secp256k1::Error),
|
||||
|
||||
#[error("Risc0 error: {0}")]
|
||||
ProgramWriteInputFailed(String),
|
||||
@ -59,13 +59,16 @@ pub enum NssaError {
|
||||
CircuitProvingError(String),
|
||||
|
||||
#[error("Invalid program bytecode")]
|
||||
InvalidProgramBytecode,
|
||||
InvalidProgramBytecode(#[source] anyhow::Error),
|
||||
|
||||
#[error("Program already exists")]
|
||||
ProgramAlreadyExists,
|
||||
|
||||
#[error("Chain of calls is too long")]
|
||||
MaxChainedCallsDepthExceeded,
|
||||
|
||||
#[error("Max account nonce reached")]
|
||||
MaxAccountNonceReached,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -83,7 +86,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure() {
|
||||
fn ensure_works() {
|
||||
assert!(test_function_ensure(true).is_ok());
|
||||
assert!(test_function_ensure(false).is_err());
|
||||
}
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
pub mod program_methods {
|
||||
include!(concat!(env!("OUT_DIR"), "/program_methods/mod.rs"));
|
||||
}
|
||||
|
||||
pub mod encoding;
|
||||
pub mod error;
|
||||
mod merkle_tree;
|
||||
pub mod privacy_preserving_transaction;
|
||||
pub mod program;
|
||||
pub mod program_deployment_transaction;
|
||||
pub mod public_transaction;
|
||||
mod signature;
|
||||
mod state;
|
||||
#![expect(
|
||||
clippy::multiple_inherent_impl,
|
||||
reason = "We prefer to group methods by functionality rather than by type for encoding"
|
||||
)]
|
||||
|
||||
pub use nssa_core::{
|
||||
SharedSecretKey,
|
||||
@ -26,3 +17,17 @@ pub use program_methods::PRIVACY_PRESERVING_CIRCUIT_ID;
|
||||
pub use public_transaction::PublicTransaction;
|
||||
pub use signature::{PrivateKey, PublicKey, Signature};
|
||||
pub use state::V02State;
|
||||
|
||||
pub mod encoding;
|
||||
pub mod error;
|
||||
mod merkle_tree;
|
||||
pub mod privacy_preserving_transaction;
|
||||
pub mod program;
|
||||
pub mod program_deployment_transaction;
|
||||
pub mod public_transaction;
|
||||
mod signature;
|
||||
mod state;
|
||||
|
||||
pub mod program_methods {
|
||||
include!(concat!(env!("OUT_DIR"), "/program_methods/mod.rs"));
|
||||
}
|
||||
|
||||
@ -1,25 +1,13 @@
|
||||
#![expect(clippy::arithmetic_side_effects, reason = "TODO: fix later")]
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
mod default_values;
|
||||
|
||||
type Value = [u8; 32];
|
||||
type Node = [u8; 32];
|
||||
|
||||
/// Compute parent as the hash of two child nodes
|
||||
fn hash_two(left: &Node, right: &Node) -> Node {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(left);
|
||||
hasher.update(right);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
fn hash_value(value: &Value) -> Node {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(value);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone, BorshSerialize, BorshDeserialize)]
|
||||
pub struct MerkleTree {
|
||||
@ -36,7 +24,8 @@ impl MerkleTree {
|
||||
|
||||
fn root_index(&self) -> usize {
|
||||
let tree_depth = self.depth();
|
||||
let capacity_depth = self.capacity.trailing_zeros() as usize;
|
||||
let capacity_depth =
|
||||
usize::try_from(self.capacity.trailing_zeros()).expect("u32 fits in usize");
|
||||
|
||||
if tree_depth == capacity_depth {
|
||||
0
|
||||
@ -48,7 +37,8 @@ impl MerkleTree {
|
||||
|
||||
/// Number of levels required to hold all nodes
|
||||
fn depth(&self) -> usize {
|
||||
self.length.next_power_of_two().trailing_zeros() as usize
|
||||
usize::try_from(self.length.next_power_of_two().trailing_zeros())
|
||||
.expect("u32 fits in usize")
|
||||
}
|
||||
|
||||
fn get_node(&self, index: usize) -> &Node {
|
||||
@ -62,7 +52,7 @@ impl MerkleTree {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
// Adjust capacity to ensure power of two
|
||||
let capacity = capacity.next_power_of_two();
|
||||
let total_depth = capacity.trailing_zeros() as usize;
|
||||
let total_depth = usize::try_from(capacity.trailing_zeros()).expect("u32 fits in usize");
|
||||
|
||||
let nodes = default_values::DEFAULT_VALUES[..=total_depth]
|
||||
.iter()
|
||||
@ -152,15 +142,33 @@ impl MerkleTree {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute parent as the hash of two child nodes
|
||||
fn hash_two(left: &Node, right: &Node) -> Node {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(left);
|
||||
hasher.update(right);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
fn hash_value(value: &Value) -> Node {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(value);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
fn prev_power_of_two(x: usize) -> usize {
|
||||
if x == 0 {
|
||||
return 0;
|
||||
}
|
||||
1 << (usize::BITS as usize - x.leading_zeros() as usize - 1)
|
||||
1 << (usize::BITS - x.leading_zeros() - 1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex_literal::hex;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl MerkleTree {
|
||||
pub fn new(values: &[Value]) -> Self {
|
||||
let mut this = Self::with_capacity(values.len());
|
||||
@ -171,11 +179,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
use hex_literal::hex;
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_empty_merkle_tree() {
|
||||
fn empty_merkle_tree() {
|
||||
let tree = MerkleTree::with_capacity(4);
|
||||
let expected_root =
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
@ -185,7 +190,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_0() {
|
||||
fn merkle_tree_0() {
|
||||
let values = [[0; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
assert_eq!(tree.root(), hash_value(&[0; 32]));
|
||||
@ -194,7 +199,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_1() {
|
||||
fn merkle_tree_1() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_root =
|
||||
@ -205,7 +210,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_2() {
|
||||
fn merkle_tree_2() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [0; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_root =
|
||||
@ -216,7 +221,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_3() {
|
||||
fn merkle_tree_3() {
|
||||
let values = [[1; 32], [2; 32], [3; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_root =
|
||||
@ -227,7 +232,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_4() {
|
||||
fn merkle_tree_4() {
|
||||
let values = [[11; 32], [12; 32], [13; 32], [14; 32], [15; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_root =
|
||||
@ -239,7 +244,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_5() {
|
||||
fn merkle_tree_5() {
|
||||
let values = [
|
||||
[11; 32], [12; 32], [12; 32], [13; 32], [14; 32], [15; 32], [15; 32], [13; 32],
|
||||
[13; 32], [15; 32], [11; 32],
|
||||
@ -253,7 +258,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merkle_tree_6() {
|
||||
fn merkle_tree_6() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32], [5; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_root =
|
||||
@ -262,7 +267,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity_4() {
|
||||
fn with_capacity_4() {
|
||||
let tree = MerkleTree::with_capacity(4);
|
||||
|
||||
assert_eq!(tree.length, 0);
|
||||
@ -277,7 +282,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity_5() {
|
||||
fn with_capacity_5() {
|
||||
let tree = MerkleTree::with_capacity(5);
|
||||
|
||||
assert_eq!(tree.length, 0);
|
||||
@ -295,7 +300,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity_6() {
|
||||
fn with_capacity_6() {
|
||||
let mut tree = MerkleTree::with_capacity(100);
|
||||
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32]];
|
||||
@ -312,7 +317,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity_7() {
|
||||
fn with_capacity_7() {
|
||||
let mut tree = MerkleTree::with_capacity(599);
|
||||
|
||||
let values = [[1; 32], [2; 32], [3; 32]];
|
||||
@ -328,7 +333,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_capacity_8() {
|
||||
fn with_capacity_8() {
|
||||
let mut tree = MerkleTree::with_capacity(1);
|
||||
|
||||
let values = [[1; 32], [2; 32], [3; 32]];
|
||||
@ -344,7 +349,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_value_1() {
|
||||
fn insert_value_1() {
|
||||
let mut tree = MerkleTree::with_capacity(1);
|
||||
|
||||
let values = [[1; 32], [2; 32], [3; 32]];
|
||||
@ -358,7 +363,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_value_2() {
|
||||
fn insert_value_2() {
|
||||
let mut tree = MerkleTree::with_capacity(1);
|
||||
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32]];
|
||||
@ -373,7 +378,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_value_3() {
|
||||
fn insert_value_3() {
|
||||
let mut tree = MerkleTree::with_capacity(1);
|
||||
|
||||
let values = [[11; 32], [12; 32], [13; 32], [14; 32], [15; 32]];
|
||||
@ -405,7 +410,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authentication_path_1() {
|
||||
fn authentication_path_1() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_authentication_path = vec![
|
||||
@ -418,7 +423,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authentication_path_2() {
|
||||
fn authentication_path_2() {
|
||||
let values = [[1; 32], [2; 32], [3; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_authentication_path = vec![
|
||||
@ -431,7 +436,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authentication_path_3() {
|
||||
fn authentication_path_3() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32], [5; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let expected_authentication_path = vec![
|
||||
@ -445,14 +450,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authentication_path_4() {
|
||||
fn authentication_path_4() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32], [5; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
assert!(tree.get_authentication_path_for(5).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authentication_path_5() {
|
||||
fn authentication_path_5() {
|
||||
let values = [[1; 32], [2; 32], [3; 32], [4; 32], [5; 32]];
|
||||
let tree = MerkleTree::new(&values);
|
||||
let index = 4;
|
||||
@ -467,7 +472,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree_with_63_insertions() {
|
||||
fn tree_with_63_insertions() {
|
||||
let values = [
|
||||
hex!("cd00acab0f45736e6c6311f1953becc0b69a062e7c2a7310875d28bdf9ef9c5b"),
|
||||
hex!("0df5a6afbcc7bf126caf7084acfc593593ab512e6ca433c61c1a922be40a04ea"),
|
||||
|
||||
@ -30,6 +30,12 @@ impl Proof {
|
||||
pub fn from_inner(inner: Vec<u8>) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool {
|
||||
let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap();
|
||||
let receipt = Receipt::new(inner, circuit_output.to_bytes());
|
||||
receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -69,20 +75,20 @@ pub fn execute_and_prove(
|
||||
program_with_dependencies: &ProgramWithDependencies,
|
||||
) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> {
|
||||
let ProgramWithDependencies {
|
||||
program,
|
||||
program: initial_program,
|
||||
dependencies,
|
||||
} = program_with_dependencies;
|
||||
let mut env_builder = ExecutorEnv::builder();
|
||||
let mut program_outputs = Vec::new();
|
||||
|
||||
let initial_call = ChainedCall {
|
||||
program_id: program.id(),
|
||||
program_id: initial_program.id(),
|
||||
instruction_data,
|
||||
pre_states,
|
||||
pda_seeds: vec![],
|
||||
};
|
||||
|
||||
let mut chained_calls = VecDeque::from_iter([(initial_call, program)]);
|
||||
let mut chained_calls = VecDeque::from_iter([(initial_call, initial_program)]);
|
||||
let mut chain_calls_counter = 0;
|
||||
while let Some((chained_call, program)) = chained_calls.pop_front() {
|
||||
if chain_calls_counter >= MAX_NUMBER_CHAINED_CALLS {
|
||||
@ -113,7 +119,9 @@ pub fn execute_and_prove(
|
||||
chained_calls.push_front((new_call, next_program));
|
||||
}
|
||||
|
||||
chain_calls_counter += 1;
|
||||
chain_calls_counter = chain_calls_counter
|
||||
.checked_add(1)
|
||||
.expect("we check the max depth at the beginning of the loop");
|
||||
}
|
||||
|
||||
let circuit_input = PrivacyPreservingCircuitInput {
|
||||
@ -163,16 +171,10 @@ fn execute_and_prove_program(
|
||||
.receipt)
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool {
|
||||
let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap();
|
||||
let receipt = Receipt::new(inner, circuit_output.to_bytes());
|
||||
receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")]
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier,
|
||||
account::{Account, AccountId, AccountWithMetadata, data::Data},
|
||||
|
||||
@ -4,7 +4,7 @@ use nssa_core::{
|
||||
account::{Account, Nonce},
|
||||
encryption::{Ciphertext, EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
use crate::{AccountId, error::NssaError};
|
||||
|
||||
@ -56,7 +56,7 @@ pub struct Message {
|
||||
|
||||
impl std::fmt::Debug for Message {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
struct HexDigest<'a>(&'a [u8; 32]);
|
||||
struct HexDigest<'arr>(&'arr [u8; 32]);
|
||||
impl std::fmt::Debug for HexDigest<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", hex::encode(self.0))
|
||||
@ -120,7 +120,7 @@ pub mod tests {
|
||||
account::Account,
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
use crate::{
|
||||
AccountId,
|
||||
@ -165,7 +165,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypted_account_data_constructor() {
|
||||
fn encrypted_account_data_constructor() {
|
||||
let npk = NullifierPublicKey::from(&[1; 32]);
|
||||
let vpk = ViewingPublicKey::from_scalar([2; 32]);
|
||||
let account = Account::default();
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
pub use message::Message;
|
||||
pub use transaction::PrivacyPreservingTransaction;
|
||||
pub use witness_set::WitnessSet;
|
||||
|
||||
pub mod message;
|
||||
pub mod transaction;
|
||||
pub mod witness_set;
|
||||
|
||||
pub mod circuit;
|
||||
|
||||
pub use message::Message;
|
||||
pub use transaction::PrivacyPreservingTransaction;
|
||||
pub use witness_set::WitnessSet;
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
@ -188,7 +191,6 @@ fn check_privacy_preserving_circuit_proof_is_valid(
|
||||
.ok_or(NssaError::InvalidPrivacyPreservingProof)
|
||||
}
|
||||
|
||||
use std::hash::Hash;
|
||||
fn n_unique<T: Eq + Hash>(data: &[T]) -> usize {
|
||||
let set: HashSet<&T> = data.iter().collect();
|
||||
set.len()
|
||||
@ -225,7 +227,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_privacy_preserving_transaction_encoding_bytes_roundtrip() {
|
||||
fn privacy_preserving_transaction_encoding_bytes_roundtrip() {
|
||||
let tx = transaction_for_tests();
|
||||
let bytes = tx.to_bytes();
|
||||
let tx_from_bytes = PrivacyPreservingTransaction::from_bytes(&bytes).unwrap();
|
||||
|
||||
@ -24,10 +24,10 @@ pub struct Program {
|
||||
impl Program {
|
||||
pub fn new(bytecode: Vec<u8>) -> Result<Self, NssaError> {
|
||||
let binary = risc0_binfmt::ProgramBinary::decode(&bytecode)
|
||||
.map_err(|_| NssaError::InvalidProgramBytecode)?;
|
||||
.map_err(NssaError::InvalidProgramBytecode)?;
|
||||
let id = binary
|
||||
.compute_image_id()
|
||||
.map_err(|_| NssaError::InvalidProgramBytecode)?
|
||||
.map_err(NssaError::InvalidProgramBytecode)?
|
||||
.into();
|
||||
Ok(Self { elf: bytecode, id })
|
||||
}
|
||||
@ -117,6 +117,7 @@ impl Program {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::non_ascii_literal, reason = "More readable")]
|
||||
pub fn pinata_token() -> Self {
|
||||
use crate::program_methods::PINATA_TOKEN_ELF;
|
||||
Self::new(PINATA_TOKEN_ELF.to_vec()).expect("Piñata program must be a valid R0BF file")
|
||||
@ -286,7 +287,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_program_execution() {
|
||||
fn program_execution() {
|
||||
let program = Program::simple_balance_transfer();
|
||||
let balance_to_move: u128 = 11_223_344_556_677;
|
||||
let instruction_data = Program::serialize_instruction(balance_to_move).unwrap();
|
||||
@ -320,7 +321,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builtin_programs() {
|
||||
fn builtin_programs() {
|
||||
let auth_transfer_program = Program::authenticated_transfer_program();
|
||||
let token_program = Program::token();
|
||||
let pinata_program = Program::pinata();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
mod message;
|
||||
mod transaction;
|
||||
|
||||
pub use message::Message;
|
||||
pub use transaction::ProgramDeploymentTransaction;
|
||||
|
||||
mod message;
|
||||
mod transaction;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
mod message;
|
||||
mod transaction;
|
||||
mod witness_set;
|
||||
|
||||
pub use message::Message;
|
||||
pub use transaction::PublicTransaction;
|
||||
pub use witness_set::WitnessSet;
|
||||
|
||||
mod message;
|
||||
mod transaction;
|
||||
mod witness_set;
|
||||
|
||||
@ -6,7 +6,7 @@ use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
program::{ChainedCall, DEFAULT_PROGRAM_ID, validate_execution},
|
||||
};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
use crate::{
|
||||
V02State, ensure,
|
||||
@ -216,7 +216,9 @@ impl PublicTransaction {
|
||||
chained_calls.push_front((new_call, Some(chained_call.program_id)));
|
||||
}
|
||||
|
||||
chain_calls_counter += 1;
|
||||
chain_calls_counter = chain_calls_counter
|
||||
.checked_add(1)
|
||||
.expect("we check the max depth at the beginning of the loop");
|
||||
}
|
||||
|
||||
// Check that all modified uninitialized accounts where claimed
|
||||
@ -242,7 +244,7 @@ impl PublicTransaction {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
use crate::{
|
||||
AccountId, PrivateKey, PublicKey, PublicTransaction, Signature, V02State,
|
||||
@ -282,7 +284,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_constructor() {
|
||||
fn new_constructor() {
|
||||
let tx = transaction_for_tests();
|
||||
let message = tx.message().clone();
|
||||
let witness_set = tx.witness_set().clone();
|
||||
@ -292,19 +294,19 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_getter() {
|
||||
fn message_getter() {
|
||||
let tx = transaction_for_tests();
|
||||
assert_eq!(&tx.message, tx.message());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_witness_set_getter() {
|
||||
fn witness_set_getter() {
|
||||
let tx = transaction_for_tests();
|
||||
assert_eq!(&tx.witness_set, tx.witness_set());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signer_account_ids() {
|
||||
fn signer_account_ids() {
|
||||
let tx = transaction_for_tests();
|
||||
let expected_signer_account_ids = vec![
|
||||
AccountId::new([
|
||||
@ -321,7 +323,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_public_transaction_encoding_bytes_roundtrip() {
|
||||
fn public_transaction_encoding_bytes_roundtrip() {
|
||||
let tx = transaction_for_tests();
|
||||
let bytes = tx.to_bytes();
|
||||
let tx_from_bytes = PublicTransaction::from_bytes(&bytes).unwrap();
|
||||
@ -329,7 +331,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_is_sha256_of_transaction_bytes() {
|
||||
fn hash_is_sha256_of_transaction_bytes() {
|
||||
let tx = transaction_for_tests();
|
||||
let hash = tx.hash();
|
||||
let expected_hash: [u8; 32] = {
|
||||
@ -342,7 +344,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_account_id_list_cant_have_duplicates() {
|
||||
fn account_id_list_cant_have_duplicates() {
|
||||
let (key1, _, addr1, _) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0, 0];
|
||||
@ -362,7 +364,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_of_nonces_must_match_number_of_signatures() {
|
||||
fn number_of_nonces_must_match_number_of_signatures() {
|
||||
let (key1, key2, addr1, addr2) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0];
|
||||
@ -382,7 +384,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_signatures_must_be_valid() {
|
||||
fn all_signatures_must_be_valid() {
|
||||
let (key1, key2, addr1, addr2) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0, 0];
|
||||
@ -403,7 +405,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonces_must_match_the_state_current_nonces() {
|
||||
fn nonces_must_match_the_state_current_nonces() {
|
||||
let (key1, key2, addr1, addr2) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0, 1];
|
||||
@ -423,7 +425,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_program_id_must_belong_to_bulitin_program_ids() {
|
||||
fn program_id_must_belong_to_bulitin_program_ids() {
|
||||
let (key1, key2, addr1, addr2) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0, 0];
|
||||
|
||||
@ -60,7 +60,7 @@ mod tests {
|
||||
use crate::AccountId;
|
||||
|
||||
#[test]
|
||||
fn test_for_message_constructor() {
|
||||
fn for_message_constructor() {
|
||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let pubkey1 = PublicKey::new_from_private_key(&key1);
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
use crate::{PrivateKey, PublicKey, Signature};
|
||||
|
||||
fn hex_to_bytes<const N: usize>(hex: &str) -> [u8; N] {
|
||||
hex::decode(hex).unwrap().try_into().unwrap()
|
||||
}
|
||||
|
||||
pub struct TestVector {
|
||||
pub seckey: Option<PrivateKey>,
|
||||
pub pubkey: PublicKey,
|
||||
@ -365,3 +361,7 @@ pub fn test_vectors() -> Vec<TestVector> {
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn hex_to_bytes<const N: usize>(hex: &str) -> [u8; N] {
|
||||
hex::decode(hex).unwrap().try_into().unwrap()
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
mod private_key;
|
||||
mod public_key;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
pub use private_key::PrivateKey;
|
||||
pub use public_key::PublicKey;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
|
||||
mod private_key;
|
||||
mod public_key;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Signature {
|
||||
@ -20,7 +20,7 @@ impl std::fmt::Debug for Signature {
|
||||
impl Signature {
|
||||
#[must_use]
|
||||
pub fn new(key: &PrivateKey, message: &[u8]) -> Self {
|
||||
let mut aux_random = [0u8; 32];
|
||||
let mut aux_random = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut aux_random);
|
||||
Self::new_with_aux_random(key, message, aux_random)
|
||||
}
|
||||
@ -64,7 +64,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_generation_from_bip340_test_vectors() {
|
||||
fn signature_generation_from_bip340_test_vectors() {
|
||||
for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() {
|
||||
let Some(private_key) = test_vector.seckey else {
|
||||
continue;
|
||||
@ -87,7 +87,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_verification_from_bip340_test_vectors() {
|
||||
fn signature_verification_from_bip340_test_vectors() {
|
||||
for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() {
|
||||
let message = test_vector.message.unwrap_or(vec![]);
|
||||
let expected_result = test_vector.verification_result;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use rand::{Rng, rngs::OsRng};
|
||||
use rand::{Rng as _, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::NssaError;
|
||||
@ -42,13 +42,13 @@ impl PrivateKey {
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_value_getter() {
|
||||
fn value_getter() {
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
assert_eq!(key.value(), &key.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_produce_key() {
|
||||
fn produce_key() {
|
||||
let _key = PrivateKey::new_os_random();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::account::AccountId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
use crate::{PrivateKey, error::NssaError};
|
||||
|
||||
@ -16,15 +16,10 @@ impl std::fmt::Debug for PublicKey {
|
||||
|
||||
impl BorshDeserialize for PublicKey {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut buf = [0u8; 32];
|
||||
let mut buf = [0_u8; 32];
|
||||
reader.read_exact(&mut buf)?;
|
||||
|
||||
Self::try_new(buf).map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Invalid public key: not a valid point",
|
||||
)
|
||||
})
|
||||
Self::try_new(buf).map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +39,7 @@ impl PublicKey {
|
||||
pub fn try_new(value: [u8; 32]) -> Result<Self, NssaError> {
|
||||
// Check point is valid
|
||||
let _ = secp256k1::XOnlyPublicKey::from_byte_array(value)
|
||||
.map_err(|_| NssaError::InvalidPublicKey)?;
|
||||
.map_err(NssaError::InvalidPublicKey)?;
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
||||
@ -71,7 +66,7 @@ mod test {
|
||||
use crate::{PublicKey, error::NssaError, signature::bip340_test_vectors};
|
||||
|
||||
#[test]
|
||||
fn test_try_new_invalid_public_key_from_bip340_test_vectors_5() {
|
||||
fn try_new_invalid_public_key_from_bip340_test_vectors_5() {
|
||||
let value_invalid_key = [
|
||||
238, 253, 234, 76, 219, 103, 119, 80, 164, 32, 254, 232, 7, 234, 207, 33, 235, 152,
|
||||
152, 174, 121, 185, 118, 135, 102, 228, 250, 160, 74, 45, 74, 52,
|
||||
@ -79,11 +74,11 @@ mod test {
|
||||
|
||||
let result = PublicKey::try_new(value_invalid_key);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidPublicKey)));
|
||||
assert!(matches!(result, Err(NssaError::InvalidPublicKey(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_new_invalid_public_key_from_bip340_test_vector_14() {
|
||||
fn try_new_invalid_public_key_from_bip340_test_vector_14() {
|
||||
let value_invalid_key = [
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 252, 48,
|
||||
@ -91,11 +86,11 @@ mod test {
|
||||
|
||||
let result = PublicKey::try_new(value_invalid_key);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidPublicKey)));
|
||||
assert!(matches!(result, Err(NssaError::InvalidPublicKey(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_new_valid_public_keys() {
|
||||
fn try_new_valid_public_keys() {
|
||||
for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() {
|
||||
let expected_public_key = test_vector.pubkey;
|
||||
let public_key = PublicKey::try_new(*expected_public_key.value()).unwrap();
|
||||
@ -104,7 +99,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_public_key_generation_from_bip340_test_vectors() {
|
||||
fn public_key_generation_from_bip340_test_vectors() {
|
||||
for (i, test_vector) in bip340_test_vectors::test_vectors().into_iter().enumerate() {
|
||||
let Some(private_key) = &test_vector.seckey else {
|
||||
continue;
|
||||
@ -119,7 +114,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_correct_ser_deser_roundtrip() {
|
||||
fn correct_ser_deser_roundtrip() {
|
||||
let pub_key = PublicKey::try_new([42; 32]).unwrap();
|
||||
|
||||
let pub_key_borsh_ser = borsh::to_vec(&pub_key).unwrap();
|
||||
|
||||
3990
nssa/src/state.rs
3990
nssa/src/state.rs
File diff suppressed because it is too large
Load Diff
@ -31,24 +31,24 @@ fn transfer(
|
||||
// Continue only if the sender has authorized this operation
|
||||
assert!(sender.is_authorized, "Sender must be authorized");
|
||||
|
||||
// Continue only if the sender has enough balance
|
||||
assert!(
|
||||
sender.account.balance >= balance_to_move,
|
||||
"Sender has insufficient balance"
|
||||
);
|
||||
|
||||
// Create accounts post states, with updated balances
|
||||
let sender_post = {
|
||||
// Modify sender's balance
|
||||
let mut sender_post_account = sender.account;
|
||||
sender_post_account.balance -= balance_to_move;
|
||||
sender_post_account.balance = sender_post_account
|
||||
.balance
|
||||
.checked_sub(balance_to_move)
|
||||
.expect("Sender has insufficient balance");
|
||||
AccountPostState::new(sender_post_account)
|
||||
};
|
||||
|
||||
let recipient_post = {
|
||||
// Modify recipient's balance
|
||||
let mut recipient_post_account = recipient.account;
|
||||
recipient_post_account.balance += balance_to_move;
|
||||
recipient_post_account.balance = recipient_post_account
|
||||
.balance
|
||||
.checked_add(balance_to_move)
|
||||
.expect("Recipient balance overflow");
|
||||
|
||||
// Claim recipient account if it has default program owner
|
||||
if recipient_post_account.program_owner == DEFAULT_PROGRAM_ID {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use nssa_core::program::{AccountPostState, ProgramInput, read_nssa_inputs, write_nssa_outputs};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
const PRIZE: u128 = 150;
|
||||
|
||||
@ -28,7 +28,7 @@ impl Challenge {
|
||||
bytes[..32].copy_from_slice(&self.seed);
|
||||
bytes[32..].copy_from_slice(&solution.to_le_bytes());
|
||||
let digest: [u8; 32] = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
let difficulty = self.difficulty as usize;
|
||||
let difficulty = usize::from(self.difficulty);
|
||||
digest[..difficulty].iter().all(|&b| b == 0)
|
||||
}
|
||||
|
||||
@ -64,13 +64,19 @@ fn main() {
|
||||
|
||||
let mut pinata_post = pinata.account.clone();
|
||||
let mut winner_post = winner.account.clone();
|
||||
pinata_post.balance -= PRIZE;
|
||||
pinata_post.balance = pinata_post
|
||||
.balance
|
||||
.checked_sub(PRIZE)
|
||||
.expect("Not enough balance in the pinata");
|
||||
pinata_post.data = data
|
||||
.next_data()
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.expect("33 bytes should fit into Data");
|
||||
winner_post.balance += PRIZE;
|
||||
winner_post.balance = winner_post
|
||||
.balance
|
||||
.checked_add(PRIZE)
|
||||
.expect("Overflow when adding prize to winner");
|
||||
|
||||
write_nssa_outputs(
|
||||
instruction_words,
|
||||
|
||||
@ -5,7 +5,7 @@ use nssa_core::{
|
||||
write_nssa_outputs_with_chained_call,
|
||||
},
|
||||
};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
const PRIZE: u128 = 150;
|
||||
|
||||
@ -34,7 +34,7 @@ impl Challenge {
|
||||
bytes[..32].copy_from_slice(&self.seed);
|
||||
bytes[32..].copy_from_slice(&solution.to_le_bytes());
|
||||
let digest: [u8; 32] = Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap();
|
||||
let difficulty = self.difficulty as usize;
|
||||
let difficulty = usize::from(self.difficulty);
|
||||
digest[..difficulty].iter().all(|&b| b == 0)
|
||||
}
|
||||
|
||||
|
||||
@ -16,31 +16,6 @@ use nssa_core::{
|
||||
};
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
|
||||
fn main() {
|
||||
let PrivacyPreservingCircuitInput {
|
||||
program_outputs,
|
||||
visibility_mask,
|
||||
private_account_nonces,
|
||||
private_account_keys,
|
||||
private_account_nsks,
|
||||
private_account_membership_proofs,
|
||||
program_id,
|
||||
} = env::read();
|
||||
|
||||
let execution_state = ExecutionState::derive_from_outputs(program_id, program_outputs);
|
||||
|
||||
let output = compute_circuit_output(
|
||||
execution_state,
|
||||
&visibility_mask,
|
||||
&private_account_nonces,
|
||||
&private_account_keys,
|
||||
&private_account_nsks,
|
||||
&private_account_membership_proofs,
|
||||
);
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
/// State of the involved accounts before and after program execution.
|
||||
struct ExecutionState {
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
@ -117,7 +92,9 @@ impl ExecutionState {
|
||||
program_output.pre_states,
|
||||
program_output.post_states,
|
||||
);
|
||||
chain_calls_counter += 1;
|
||||
chain_calls_counter = chain_calls_counter.checked_add(1).expect(
|
||||
"Chain calls counter should not overflow as it checked before incrementing",
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
@ -249,10 +226,10 @@ fn compute_circuit_output(
|
||||
let mut private_membership_proofs_iter = private_account_membership_proofs.iter();
|
||||
|
||||
let mut output_index = 0;
|
||||
for (visibility_mask, (pre_state, post_state)) in
|
||||
for (account_visibility_mask, (pre_state, post_state)) in
|
||||
visibility_mask.iter().copied().zip(states_iter)
|
||||
{
|
||||
match visibility_mask {
|
||||
match account_visibility_mask {
|
||||
0 => {
|
||||
// Public account
|
||||
output.public_pre_states.push(pre_state);
|
||||
@ -269,7 +246,7 @@ fn compute_circuit_output(
|
||||
"AccountId mismatch"
|
||||
);
|
||||
|
||||
let new_nullifier = if visibility_mask == 1 {
|
||||
let new_nullifier = if account_visibility_mask == 1 {
|
||||
// Private account with authentication
|
||||
|
||||
let Some(nsk) = private_nsks_iter.next() else {
|
||||
@ -347,7 +324,10 @@ fn compute_circuit_output(
|
||||
|
||||
output.new_commitments.push(commitment_post);
|
||||
output.ciphertexts.push(encrypted_account);
|
||||
output_index += 1;
|
||||
output_index = match output_index.checked_add(1) {
|
||||
Some(val) => val,
|
||||
None => panic!("Too many private accounts, output index overflow"),
|
||||
}
|
||||
}
|
||||
_ => panic!("Invalid visibility mask value"),
|
||||
}
|
||||
@ -402,3 +382,28 @@ fn compute_nullifier_and_set_digest(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let PrivacyPreservingCircuitInput {
|
||||
program_outputs,
|
||||
visibility_mask,
|
||||
private_account_nonces,
|
||||
private_account_keys,
|
||||
private_account_nsks,
|
||||
private_account_membership_proofs,
|
||||
program_id,
|
||||
} = env::read();
|
||||
|
||||
let execution_state = ExecutionState::derive_from_outputs(program_id, program_outputs);
|
||||
|
||||
let output = compute_circuit_output(
|
||||
execution_state,
|
||||
&visibility_mask,
|
||||
&private_account_nonces,
|
||||
&private_account_keys,
|
||||
&private_account_nsks,
|
||||
&private_account_membership_proofs,
|
||||
);
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ pub fn compute_pool_pda_seed(
|
||||
definition_token_a_id: AccountId,
|
||||
definition_token_b_id: AccountId,
|
||||
) -> PdaSeed {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
let (token_1, token_2) = match definition_token_a_id
|
||||
.value()
|
||||
@ -167,7 +167,7 @@ pub fn compute_vault_pda(
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId) -> PdaSeed {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
let mut bytes = [0; 64];
|
||||
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
|
||||
@ -188,7 +188,7 @@ pub fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
let mut bytes = [0; 64];
|
||||
bytes[0..32].copy_from_slice(&pool_id.to_bytes());
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
//! The AMM Program implementation.
|
||||
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
clippy::integer_division,
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "TODO: Fix later"
|
||||
)]
|
||||
|
||||
pub use amm_core as core;
|
||||
|
||||
pub mod add;
|
||||
|
||||
@ -103,7 +103,7 @@ pub fn new_definition(
|
||||
liquidity_pool_supply: initial_lp,
|
||||
reserve_a: token_a_amount.into(),
|
||||
reserve_b: token_b_amount.into(),
|
||||
fees: 0u128, // TODO: we assume all fees are 0 for now.
|
||||
fees: 0_u128, // TODO: we assume all fees are 0 for now.
|
||||
active: true,
|
||||
};
|
||||
|
||||
|
||||
@ -410,7 +410,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: BalanceForTests::user_token_a_balance(),
|
||||
@ -426,7 +426,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: BalanceForTests::user_token_b_balance(),
|
||||
@ -442,7 +442,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: BalanceForTests::vault_a_reserve_init(),
|
||||
@ -458,7 +458,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: BalanceForTests::vault_b_reserve_init(),
|
||||
@ -474,7 +474,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: BalanceForTests::vault_a_reserve_high(),
|
||||
@ -490,7 +490,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: BalanceForTests::vault_b_reserve_high(),
|
||||
@ -506,7 +506,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: BalanceForTests::vault_a_reserve_low(),
|
||||
@ -522,7 +522,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: BalanceForTests::vault_b_reserve_low(),
|
||||
@ -538,7 +538,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: 0,
|
||||
@ -554,7 +554,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: 0,
|
||||
@ -570,7 +570,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::lp_supply_init(),
|
||||
@ -587,7 +587,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::lp_supply_init(),
|
||||
@ -604,7 +604,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_lp_definition_id(),
|
||||
balance: 0,
|
||||
@ -620,7 +620,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_lp_definition_id(),
|
||||
balance: BalanceForTests::user_token_lp_balance(),
|
||||
@ -636,7 +636,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -646,7 +646,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -660,7 +660,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -670,7 +670,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: 0,
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -684,7 +684,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -694,7 +694,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: 0,
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -708,7 +708,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -718,7 +718,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::vault_a_reserve_low(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_low(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_high(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -732,7 +732,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -742,7 +742,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::vault_a_reserve_high(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_high(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_low(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -756,7 +756,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -766,7 +766,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_swap_test_1(),
|
||||
reserve_b: BalanceForTests::vault_b_swap_test_1(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -780,7 +780,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -790,7 +790,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_swap_test_2(),
|
||||
reserve_b: BalanceForTests::vault_b_swap_test_2(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -804,7 +804,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -814,7 +814,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::vault_a_reserve_low(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -828,7 +828,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -838,7 +838,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: 989,
|
||||
reserve_a: BalanceForTests::vault_a_add_successful(),
|
||||
reserve_b: BalanceForTests::vault_b_add_successful(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -852,7 +852,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -862,7 +862,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: 607,
|
||||
reserve_a: BalanceForTests::vault_a_remove_successful(),
|
||||
reserve_b: BalanceForTests::vault_b_remove_successful(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -876,7 +876,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -886,7 +886,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: false,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -900,7 +900,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -910,7 +910,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: false,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -924,7 +924,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_a_definition_id(),
|
||||
balance: BalanceForTests::vault_a_reserve_init(),
|
||||
@ -940,7 +940,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::token_b_definition_id(),
|
||||
balance: BalanceForTests::vault_b_reserve_init(),
|
||||
@ -956,7 +956,7 @@ impl AccountForTests {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: ProgramId::default(),
|
||||
balance: 0u128,
|
||||
balance: 0_u128,
|
||||
data: Data::from(&PoolDefinition {
|
||||
definition_token_a_id: IdForTests::token_a_definition_id(),
|
||||
definition_token_b_id: IdForTests::token_b_definition_id(),
|
||||
@ -966,7 +966,7 @@ impl AccountForTests {
|
||||
liquidity_pool_supply: BalanceForTests::lp_supply_init(),
|
||||
reserve_a: BalanceForTests::vault_a_reserve_init(),
|
||||
reserve_b: BalanceForTests::vault_b_reserve_init(),
|
||||
fees: 0u128,
|
||||
fees: 0_u128,
|
||||
active: true,
|
||||
}),
|
||||
nonce: 0,
|
||||
@ -978,7 +978,7 @@ impl AccountForTests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pool_pda_produces_unique_id_for_token_pair() {
|
||||
fn pool_pda_produces_unique_id_for_token_pair() {
|
||||
assert!(
|
||||
amm_core::compute_pool_pda(
|
||||
AMM_PROGRAM_ID,
|
||||
@ -994,7 +994,7 @@ fn test_pool_pda_produces_unique_id_for_token_pair() {
|
||||
|
||||
#[should_panic(expected = "Vault A was not provided")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_vault_a_omitted() {
|
||||
fn call_add_liquidity_vault_a_omitted() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_with_wrong_id(),
|
||||
@ -1011,7 +1011,7 @@ fn test_call_add_liquidity_vault_a_omitted() {
|
||||
|
||||
#[should_panic(expected = "Vault B was not provided")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_vault_b_omitted() {
|
||||
fn call_add_liquidity_vault_b_omitted() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1028,7 +1028,7 @@ fn test_call_add_liquidity_vault_b_omitted() {
|
||||
|
||||
#[should_panic(expected = "LP definition mismatch")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_lp_definition_mismatch() {
|
||||
fn call_add_liquidity_lp_definition_mismatch() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1045,7 +1045,7 @@ fn test_call_add_liquidity_lp_definition_mismatch() {
|
||||
|
||||
#[should_panic(expected = "Both max-balances must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_zero_balance_1() {
|
||||
fn call_add_liquidity_zero_balance_1() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1062,7 +1062,7 @@ fn test_call_add_liquidity_zero_balance_1() {
|
||||
|
||||
#[should_panic(expected = "Both max-balances must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_zero_balance_2() {
|
||||
fn call_add_liquidity_zero_balance_2() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1079,7 +1079,7 @@ fn test_call_add_liquidity_zero_balance_2() {
|
||||
|
||||
#[should_panic(expected = "Vaults' balances must be at least the reserve amounts")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_vault_insufficient_balance_1() {
|
||||
fn call_add_liquidity_vault_insufficient_balance_1() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init_zero(),
|
||||
@ -1096,7 +1096,7 @@ fn test_call_add_liquidity_vault_insufficient_balance_1() {
|
||||
|
||||
#[should_panic(expected = "Vaults' balances must be at least the reserve amounts")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_vault_insufficient_balance_2() {
|
||||
fn call_add_liquidity_vault_insufficient_balance_2() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1113,7 +1113,7 @@ fn test_call_add_liquidity_vault_insufficient_balance_2() {
|
||||
|
||||
#[should_panic(expected = "A trade amount is 0")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_actual_amount_zero_1() {
|
||||
fn call_add_liquidity_actual_amount_zero_1() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init_reserve_a_low(),
|
||||
AccountForTests::vault_a_init_low(),
|
||||
@ -1130,7 +1130,7 @@ fn test_call_add_liquidity_actual_amount_zero_1() {
|
||||
|
||||
#[should_panic(expected = "A trade amount is 0")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_actual_amount_zero_2() {
|
||||
fn call_add_liquidity_actual_amount_zero_2() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init_reserve_b_low(),
|
||||
AccountForTests::vault_a_init_high(),
|
||||
@ -1147,7 +1147,7 @@ fn test_call_add_liquidity_actual_amount_zero_2() {
|
||||
|
||||
#[should_panic(expected = "Reserves must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_reserves_zero_1() {
|
||||
fn call_add_liquidity_reserves_zero_1() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init_reserve_a_zero(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1164,7 +1164,7 @@ fn test_call_add_liquidity_reserves_zero_1() {
|
||||
|
||||
#[should_panic(expected = "Reserves must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_reserves_zero_2() {
|
||||
fn call_add_liquidity_reserves_zero_2() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_init_reserve_b_zero(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1181,7 +1181,7 @@ fn test_call_add_liquidity_reserves_zero_2() {
|
||||
|
||||
#[should_panic(expected = "Payable LP must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_add_liquidity_payable_lp_zero() {
|
||||
fn call_add_liquidity_payable_lp_zero() {
|
||||
let _post_states = add_liquidity(
|
||||
AccountForTests::pool_definition_add_zero_lp(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1197,7 +1197,7 @@ fn test_call_add_liquidity_payable_lp_zero() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_add_liquidity_chained_call_successsful() {
|
||||
fn call_add_liquidity_chained_call_successsful() {
|
||||
let (post_states, chained_calls) = add_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1226,7 +1226,7 @@ fn test_call_add_liquidity_chained_call_successsful() {
|
||||
|
||||
#[should_panic(expected = "Vault A was not provided")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_vault_a_omitted() {
|
||||
fn call_remove_liquidity_vault_a_omitted() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_with_wrong_id(),
|
||||
@ -1243,7 +1243,7 @@ fn test_call_remove_liquidity_vault_a_omitted() {
|
||||
|
||||
#[should_panic(expected = "Vault B was not provided")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_vault_b_omitted() {
|
||||
fn call_remove_liquidity_vault_b_omitted() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1260,7 +1260,7 @@ fn test_call_remove_liquidity_vault_b_omitted() {
|
||||
|
||||
#[should_panic(expected = "LP definition mismatch")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_lp_def_mismatch() {
|
||||
fn call_remove_liquidity_lp_def_mismatch() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1277,7 +1277,7 @@ fn test_call_remove_liquidity_lp_def_mismatch() {
|
||||
|
||||
#[should_panic(expected = "Invalid liquidity account provided")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_insufficient_liquidity_amount() {
|
||||
fn call_remove_liquidity_insufficient_liquidity_amount() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1297,7 +1297,7 @@ fn test_call_remove_liquidity_insufficient_liquidity_amount() {
|
||||
expected = "Insufficient minimal withdraw amount (Token A) provided for liquidity amount"
|
||||
)]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_insufficient_balance_1() {
|
||||
fn call_remove_liquidity_insufficient_balance_1() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1316,7 +1316,7 @@ fn test_call_remove_liquidity_insufficient_balance_1() {
|
||||
expected = "Insufficient minimal withdraw amount (Token B) provided for liquidity amount"
|
||||
)]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_insufficient_balance_2() {
|
||||
fn call_remove_liquidity_insufficient_balance_2() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1333,7 +1333,7 @@ fn test_call_remove_liquidity_insufficient_balance_2() {
|
||||
|
||||
#[should_panic(expected = "Minimum withdraw amount must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_min_bal_zero_1() {
|
||||
fn call_remove_liquidity_min_bal_zero_1() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1350,7 +1350,7 @@ fn test_call_remove_liquidity_min_bal_zero_1() {
|
||||
|
||||
#[should_panic(expected = "Minimum withdraw amount must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_min_bal_zero_2() {
|
||||
fn call_remove_liquidity_min_bal_zero_2() {
|
||||
let _post_states = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1366,7 +1366,7 @@ fn test_call_remove_liquidity_min_bal_zero_2() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_remove_liquidity_chained_call_successful() {
|
||||
fn call_remove_liquidity_chained_call_successful() {
|
||||
let (post_states, chained_calls) = remove_liquidity(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1395,7 +1395,7 @@ fn test_call_remove_liquidity_chained_call_successful() {
|
||||
|
||||
#[should_panic(expected = "Balances must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_new_definition_with_zero_balance_1() {
|
||||
fn call_new_definition_with_zero_balance_1() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1412,7 +1412,7 @@ fn test_call_new_definition_with_zero_balance_1() {
|
||||
|
||||
#[should_panic(expected = "Balances must be nonzero")]
|
||||
#[test]
|
||||
fn test_call_new_definition_with_zero_balance_2() {
|
||||
fn call_new_definition_with_zero_balance_2() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1429,7 +1429,7 @@ fn test_call_new_definition_with_zero_balance_2() {
|
||||
|
||||
#[should_panic(expected = "Cannot set up a swap for a token with itself")]
|
||||
#[test]
|
||||
fn test_call_new_definition_same_token_definition() {
|
||||
fn call_new_definition_same_token_definition() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1446,7 +1446,7 @@ fn test_call_new_definition_same_token_definition() {
|
||||
|
||||
#[should_panic(expected = "Liquidity pool Token Definition Account ID does not match PDA")]
|
||||
#[test]
|
||||
fn test_call_new_definition_wrong_liquidity_id() {
|
||||
fn call_new_definition_wrong_liquidity_id() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1463,7 +1463,7 @@ fn test_call_new_definition_wrong_liquidity_id() {
|
||||
|
||||
#[should_panic(expected = "Pool Definition Account ID does not match PDA")]
|
||||
#[test]
|
||||
fn test_call_new_definition_wrong_pool_id() {
|
||||
fn call_new_definition_wrong_pool_id() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_with_wrong_id(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1480,7 +1480,7 @@ fn test_call_new_definition_wrong_pool_id() {
|
||||
|
||||
#[should_panic(expected = "Vault ID does not match PDA")]
|
||||
#[test]
|
||||
fn test_call_new_definition_wrong_vault_id_1() {
|
||||
fn call_new_definition_wrong_vault_id_1() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_with_wrong_id(),
|
||||
@ -1497,7 +1497,7 @@ fn test_call_new_definition_wrong_vault_id_1() {
|
||||
|
||||
#[should_panic(expected = "Vault ID does not match PDA")]
|
||||
#[test]
|
||||
fn test_call_new_definition_wrong_vault_id_2() {
|
||||
fn call_new_definition_wrong_vault_id_2() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1514,7 +1514,7 @@ fn test_call_new_definition_wrong_vault_id_2() {
|
||||
|
||||
#[should_panic(expected = "Cannot initialize an active Pool Definition")]
|
||||
#[test]
|
||||
fn test_call_new_definition_cannot_initialize_active_pool() {
|
||||
fn call_new_definition_cannot_initialize_active_pool() {
|
||||
let _post_states = new_definition(
|
||||
AccountForTests::pool_definition_active(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1531,7 +1531,7 @@ fn test_call_new_definition_cannot_initialize_active_pool() {
|
||||
|
||||
#[should_panic(expected = "Cannot initialize an active Pool Definition")]
|
||||
#[test]
|
||||
fn test_call_new_definition_chained_call_successful() {
|
||||
fn call_new_definition_chained_call_successful() {
|
||||
let (post_states, chained_calls) = new_definition(
|
||||
AccountForTests::pool_definition_active(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1560,7 +1560,7 @@ fn test_call_new_definition_chained_call_successful() {
|
||||
|
||||
#[should_panic(expected = "AccountId is not a token type for the pool")]
|
||||
#[test]
|
||||
fn test_call_swap_incorrect_token_type() {
|
||||
fn call_swap_incorrect_token_type() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1575,7 +1575,7 @@ fn test_call_swap_incorrect_token_type() {
|
||||
|
||||
#[should_panic(expected = "Vault A was not provided")]
|
||||
#[test]
|
||||
fn test_call_swap_vault_a_omitted() {
|
||||
fn call_swap_vault_a_omitted() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_with_wrong_id(),
|
||||
@ -1590,7 +1590,7 @@ fn test_call_swap_vault_a_omitted() {
|
||||
|
||||
#[should_panic(expected = "Vault B was not provided")]
|
||||
#[test]
|
||||
fn test_call_swap_vault_b_omitted() {
|
||||
fn call_swap_vault_b_omitted() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1605,7 +1605,7 @@ fn test_call_swap_vault_b_omitted() {
|
||||
|
||||
#[should_panic(expected = "Reserve for Token A exceeds vault balance")]
|
||||
#[test]
|
||||
fn test_call_swap_reserves_vault_mismatch_1() {
|
||||
fn call_swap_reserves_vault_mismatch_1() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init_low(),
|
||||
@ -1620,7 +1620,7 @@ fn test_call_swap_reserves_vault_mismatch_1() {
|
||||
|
||||
#[should_panic(expected = "Reserve for Token B exceeds vault balance")]
|
||||
#[test]
|
||||
fn test_call_swap_reserves_vault_mismatch_2() {
|
||||
fn call_swap_reserves_vault_mismatch_2() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1635,7 +1635,7 @@ fn test_call_swap_reserves_vault_mismatch_2() {
|
||||
|
||||
#[should_panic(expected = "Pool is inactive")]
|
||||
#[test]
|
||||
fn test_call_swap_ianctive() {
|
||||
fn call_swap_ianctive() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_inactive(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1650,7 +1650,7 @@ fn test_call_swap_ianctive() {
|
||||
|
||||
#[should_panic(expected = "Withdraw amount is less than minimal amount out")]
|
||||
#[test]
|
||||
fn test_call_swap_below_min_out() {
|
||||
fn call_swap_below_min_out() {
|
||||
let _post_states = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1664,7 +1664,7 @@ fn test_call_swap_below_min_out() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_swap_chained_call_successful_1() {
|
||||
fn call_swap_chained_call_successful_1() {
|
||||
let (post_states, chained_calls) = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1694,7 +1694,7 @@ fn test_call_swap_chained_call_successful_1() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_swap_chained_call_successful_2() {
|
||||
fn call_swap_chained_call_successful_2() {
|
||||
let (post_states, chained_calls) = swap(
|
||||
AccountForTests::pool_definition_init(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1724,7 +1724,7 @@ fn test_call_swap_chained_call_successful_2() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_definition_lp_asymmetric_amounts() {
|
||||
fn new_definition_lp_asymmetric_amounts() {
|
||||
let (post_states, chained_calls) = new_definition(
|
||||
AccountForTests::pool_definition_inactive(),
|
||||
AccountForTests::vault_a_init(),
|
||||
@ -1751,10 +1751,10 @@ fn test_new_definition_lp_asymmetric_amounts() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_definition_lp_symmetric_amounts() {
|
||||
fn new_definition_lp_symmetric_amounts() {
|
||||
// token_a=100, token_b=100 → LP=sqrt(10_000)=100
|
||||
let token_a_amount = 100u128;
|
||||
let token_b_amount = 100u128;
|
||||
let token_a_amount = 100_u128;
|
||||
let token_b_amount = 100_u128;
|
||||
let expected_lp = (token_a_amount * token_b_amount).isqrt();
|
||||
assert_eq!(expected_lp, 100);
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ pub fn new_definition_with_metadata(
|
||||
standard: metadata.standard,
|
||||
uri: metadata.uri,
|
||||
creators: metadata.creators,
|
||||
primary_sale_date: 0u64, // TODO #261: future works to implement this
|
||||
primary_sale_date: 0_u64, // TODO #261: future works to implement this
|
||||
};
|
||||
|
||||
let mut definition_target_account_post = definition_target_account.account;
|
||||
|
||||
@ -37,7 +37,7 @@ pub fn print_nft(
|
||||
*print_balance > 1,
|
||||
"Insufficient balance to print another NFT copy"
|
||||
);
|
||||
*print_balance -= 1;
|
||||
*print_balance = print_balance.checked_sub(1).expect("Checked above");
|
||||
|
||||
let mut master_account_post = master_account.account;
|
||||
master_account_post.data = Data::from(&master_account_data);
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
#![cfg(test)]
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "We don't care about it in tests"
|
||||
)]
|
||||
|
||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata, Data};
|
||||
use token_core::{
|
||||
@ -25,8 +30,8 @@ impl AccountForTests {
|
||||
fn definition_account_auth() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply(),
|
||||
@ -42,8 +47,8 @@ impl AccountForTests {
|
||||
fn definition_account_without_auth() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply(),
|
||||
@ -59,8 +64,8 @@ impl AccountForTests {
|
||||
fn holding_different_definition() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id_diff(),
|
||||
balance: BalanceForTests::holding_balance(),
|
||||
@ -75,8 +80,8 @@ impl AccountForTests {
|
||||
fn holding_same_definition_with_authorization() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::holding_balance(),
|
||||
@ -91,8 +96,8 @@ impl AccountForTests {
|
||||
fn holding_same_definition_without_authorization() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::holding_balance(),
|
||||
@ -107,8 +112,8 @@ impl AccountForTests {
|
||||
fn holding_same_definition_without_authorization_overflow() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::init_supply(),
|
||||
@ -123,8 +128,8 @@ impl AccountForTests {
|
||||
fn definition_account_post_burn() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply_burned(),
|
||||
@ -140,8 +145,8 @@ impl AccountForTests {
|
||||
fn holding_account_post_burn() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::holding_balance_burned(),
|
||||
@ -164,8 +169,8 @@ impl AccountForTests {
|
||||
fn init_mint() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [0u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [0_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::mint_success(),
|
||||
@ -180,8 +185,8 @@ impl AccountForTests {
|
||||
fn holding_account_same_definition_mint() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::holding_balance_mint(),
|
||||
@ -196,8 +201,8 @@ impl AccountForTests {
|
||||
fn definition_account_mint() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply_mint(),
|
||||
@ -213,8 +218,8 @@ impl AccountForTests {
|
||||
fn holding_same_definition_with_authorization_and_large_balance() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::mint_overflow(),
|
||||
@ -229,8 +234,8 @@ impl AccountForTests {
|
||||
fn definition_account_with_authorization_nonfungible() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::NonFungible {
|
||||
name: String::from("test"),
|
||||
printable_supply: BalanceForTests::printable_copies(),
|
||||
@ -254,8 +259,8 @@ impl AccountForTests {
|
||||
fn holding_account_init() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::init_supply(),
|
||||
@ -270,8 +275,8 @@ impl AccountForTests {
|
||||
fn definition_account_unclaimed() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [0u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [0_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply(),
|
||||
@ -287,8 +292,8 @@ impl AccountForTests {
|
||||
fn holding_account_unclaimed() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [0u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [0_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::init_supply(),
|
||||
@ -303,8 +308,8 @@ impl AccountForTests {
|
||||
fn holding_account2_init() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::init_supply(),
|
||||
@ -319,8 +324,8 @@ impl AccountForTests {
|
||||
fn holding_account2_init_post_transfer() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::recipient_post_transfer(),
|
||||
@ -335,8 +340,8 @@ impl AccountForTests {
|
||||
fn holding_account_init_post_transfer() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
balance: BalanceForTests::sender_post_transfer(),
|
||||
@ -351,8 +356,8 @@ impl AccountForTests {
|
||||
fn holding_account_master_nft() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftMaster {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
print_balance: BalanceForTests::printable_copies(),
|
||||
@ -367,8 +372,8 @@ impl AccountForTests {
|
||||
fn holding_account_master_nft_insufficient_balance() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftMaster {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
print_balance: 1,
|
||||
@ -383,8 +388,8 @@ impl AccountForTests {
|
||||
fn holding_account_master_nft_after_print() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftMaster {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
print_balance: BalanceForTests::printable_copies() - 1,
|
||||
@ -399,8 +404,8 @@ impl AccountForTests {
|
||||
fn holding_account_printed_nft() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [0u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [0_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftPrintedCopy {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
owned: true,
|
||||
@ -415,8 +420,8 @@ impl AccountForTests {
|
||||
fn holding_account_with_master_nft_transferred_to() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [0u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [0_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftMaster {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
print_balance: BalanceForTests::printable_copies(),
|
||||
@ -431,8 +436,8 @@ impl AccountForTests {
|
||||
fn holding_account_master_nft_post_transfer() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
balance: 0u128,
|
||||
program_owner: [5_u32; 8],
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::NftMaster {
|
||||
definition_id: IdForTests::pool_definition_id(),
|
||||
print_balance: 0,
|
||||
@ -523,7 +528,7 @@ impl IdForTests {
|
||||
|
||||
#[should_panic(expected = "Definition target account must have default values")]
|
||||
#[test]
|
||||
fn test_new_definition_non_default_first_account_should_fail() {
|
||||
fn new_definition_non_default_first_account_should_fail() {
|
||||
let definition_account = AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
@ -547,7 +552,7 @@ fn test_new_definition_non_default_first_account_should_fail() {
|
||||
|
||||
#[should_panic(expected = "Holding target account must have default values")]
|
||||
#[test]
|
||||
fn test_new_definition_non_default_second_account_should_fail() {
|
||||
fn new_definition_non_default_second_account_should_fail() {
|
||||
let definition_account = AccountWithMetadata {
|
||||
account: Account::default(),
|
||||
is_authorized: true,
|
||||
@ -570,7 +575,7 @@ fn test_new_definition_non_default_second_account_should_fail() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_definition_with_valid_inputs_succeeds() {
|
||||
fn new_definition_with_valid_inputs_succeeds() {
|
||||
let definition_account = AccountForTests::definition_account_uninit();
|
||||
let holding_account = AccountForTests::holding_account_uninit();
|
||||
|
||||
@ -595,7 +600,7 @@ fn test_new_definition_with_valid_inputs_succeeds() {
|
||||
|
||||
#[should_panic(expected = "Sender and recipient definition id mismatch")]
|
||||
#[test]
|
||||
fn test_transfer_with_different_definition_ids_should_fail() {
|
||||
fn transfer_with_different_definition_ids_should_fail() {
|
||||
let sender = AccountForTests::holding_same_definition_with_authorization();
|
||||
let recipient = AccountForTests::holding_different_definition();
|
||||
let _post_states = transfer(sender, recipient, 10);
|
||||
@ -603,7 +608,7 @@ fn test_transfer_with_different_definition_ids_should_fail() {
|
||||
|
||||
#[should_panic(expected = "Insufficient balance")]
|
||||
#[test]
|
||||
fn test_transfer_with_insufficient_balance_should_fail() {
|
||||
fn transfer_with_insufficient_balance_should_fail() {
|
||||
let sender = AccountForTests::holding_same_definition_with_authorization();
|
||||
let recipient = AccountForTests::holding_account_same_definition_mint();
|
||||
// Attempt to transfer more than balance
|
||||
@ -612,14 +617,14 @@ fn test_transfer_with_insufficient_balance_should_fail() {
|
||||
|
||||
#[should_panic(expected = "Sender authorization is missing")]
|
||||
#[test]
|
||||
fn test_transfer_without_sender_authorization_should_fail() {
|
||||
fn transfer_without_sender_authorization_should_fail() {
|
||||
let sender = AccountForTests::holding_same_definition_without_authorization();
|
||||
let recipient = AccountForTests::holding_account_uninit();
|
||||
let _post_states = transfer(sender, recipient, 37);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_with_valid_inputs_succeeds() {
|
||||
fn transfer_with_valid_inputs_succeeds() {
|
||||
let sender = AccountForTests::holding_account_init();
|
||||
let recipient = AccountForTests::holding_account2_init();
|
||||
let post_states = transfer(sender, recipient, BalanceForTests::transfer_amount());
|
||||
@ -637,7 +642,7 @@ fn test_transfer_with_valid_inputs_succeeds() {
|
||||
|
||||
#[should_panic(expected = "Invalid balance for NFT Master transfer")]
|
||||
#[test]
|
||||
fn test_transfer_with_master_nft_invalid_balance() {
|
||||
fn transfer_with_master_nft_invalid_balance() {
|
||||
let sender = AccountForTests::holding_account_master_nft();
|
||||
let recipient = AccountForTests::holding_account_uninit();
|
||||
let _post_states = transfer(sender, recipient, BalanceForTests::transfer_amount());
|
||||
@ -645,14 +650,14 @@ fn test_transfer_with_master_nft_invalid_balance() {
|
||||
|
||||
#[should_panic(expected = "Invalid balance in recipient account for NFT transfer")]
|
||||
#[test]
|
||||
fn test_transfer_with_master_nft_invalid_recipient_balance() {
|
||||
fn transfer_with_master_nft_invalid_recipient_balance() {
|
||||
let sender = AccountForTests::holding_account_master_nft();
|
||||
let recipient = AccountForTests::holding_account_with_master_nft_transferred_to();
|
||||
let _post_states = transfer(sender, recipient, BalanceForTests::printable_copies());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_with_master_nft_success() {
|
||||
fn transfer_with_master_nft_success() {
|
||||
let sender = AccountForTests::holding_account_master_nft();
|
||||
let recipient = AccountForTests::holding_account_uninit();
|
||||
let post_states = transfer(sender, recipient, BalanceForTests::printable_copies());
|
||||
@ -669,7 +674,7 @@ fn test_transfer_with_master_nft_success() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_initialize_account_succeeds() {
|
||||
fn token_initialize_account_succeeds() {
|
||||
let sender = AccountForTests::holding_account_init();
|
||||
let recipient = AccountForTests::holding_account2_init();
|
||||
let post_states = transfer(sender, recipient, BalanceForTests::transfer_amount());
|
||||
@ -687,7 +692,7 @@ fn test_token_initialize_account_succeeds() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
||||
fn test_burn_mismatch_def() {
|
||||
fn burn_mismatch_def() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_different_definition();
|
||||
let _post_states = burn(
|
||||
@ -699,7 +704,7 @@ fn test_burn_mismatch_def() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Authorization is missing")]
|
||||
fn test_burn_missing_authorization() {
|
||||
fn burn_missing_authorization() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||
let _post_states = burn(
|
||||
@ -711,7 +716,7 @@ fn test_burn_missing_authorization() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Insufficient balance to burn")]
|
||||
fn test_burn_insufficient_balance() {
|
||||
fn burn_insufficient_balance() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_with_authorization();
|
||||
let _post_states = burn(
|
||||
@ -723,7 +728,7 @@ fn test_burn_insufficient_balance() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Total supply underflow")]
|
||||
fn test_burn_total_supply_underflow() {
|
||||
fn burn_total_supply_underflow() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account =
|
||||
AccountForTests::holding_same_definition_with_authorization_and_large_balance();
|
||||
@ -735,7 +740,7 @@ fn test_burn_total_supply_underflow() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_burn_success() {
|
||||
fn burn_success() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_with_authorization();
|
||||
let post_states = burn(
|
||||
@ -758,7 +763,7 @@ fn test_burn_success() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Holding account must be valid")]
|
||||
fn test_mint_not_valid_holding_account() {
|
||||
fn mint_not_valid_holding_account() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::definition_account_without_auth();
|
||||
let _post_states = mint(
|
||||
@ -770,7 +775,7 @@ fn test_mint_not_valid_holding_account() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Definition account must be valid")]
|
||||
fn test_mint_not_valid_definition_account() {
|
||||
fn mint_not_valid_definition_account() {
|
||||
let definition_account = AccountForTests::holding_same_definition_with_authorization();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||
let _post_states = mint(
|
||||
@ -782,7 +787,7 @@ fn test_mint_not_valid_definition_account() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Definition authorization is missing")]
|
||||
fn test_mint_missing_authorization() {
|
||||
fn mint_missing_authorization() {
|
||||
let definition_account = AccountForTests::definition_account_without_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||
let _post_states = mint(
|
||||
@ -794,7 +799,7 @@ fn test_mint_missing_authorization() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
||||
fn test_mint_mismatched_token_definition() {
|
||||
fn mint_mismatched_token_definition() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_different_definition();
|
||||
let _post_states = mint(
|
||||
@ -805,7 +810,7 @@ fn test_mint_mismatched_token_definition() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mint_success() {
|
||||
fn mint_success() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||
let post_states = mint(
|
||||
@ -827,7 +832,7 @@ fn test_mint_success() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mint_uninit_holding_success() {
|
||||
fn mint_uninit_holding_success() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_account_uninit();
|
||||
let post_states = mint(
|
||||
@ -851,7 +856,7 @@ fn test_mint_uninit_holding_success() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Total supply overflow")]
|
||||
fn test_mint_total_supply_overflow() {
|
||||
fn mint_total_supply_overflow() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||
let _post_states = mint(
|
||||
@ -863,7 +868,7 @@ fn test_mint_total_supply_overflow() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Balance overflow on minting")]
|
||||
fn test_mint_holding_account_overflow() {
|
||||
fn mint_holding_account_overflow() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_same_definition_without_authorization_overflow();
|
||||
let _post_states = mint(
|
||||
@ -875,7 +880,7 @@ fn test_mint_holding_account_overflow() {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Cannot mint additional supply for Non-Fungible Tokens")]
|
||||
fn test_mint_cannot_mint_unmintable_tokens() {
|
||||
fn mint_cannot_mint_unmintable_tokens() {
|
||||
let definition_account = AccountForTests::definition_account_with_authorization_nonfungible();
|
||||
let holding_account = AccountForTests::holding_account_master_nft();
|
||||
let _post_states = mint(
|
||||
@ -887,7 +892,7 @@ fn test_mint_cannot_mint_unmintable_tokens() {
|
||||
|
||||
#[should_panic(expected = "Definition target account must have default values")]
|
||||
#[test]
|
||||
fn test_call_new_definition_metadata_with_init_definition() {
|
||||
fn call_new_definition_metadata_with_init_definition() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let metadata_account = AccountWithMetadata {
|
||||
account: Account::default(),
|
||||
@ -901,12 +906,12 @@ fn test_call_new_definition_metadata_with_init_definition() {
|
||||
};
|
||||
let new_definition = NewTokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: 15u128,
|
||||
total_supply: 15_u128,
|
||||
};
|
||||
let metadata = NewTokenMetadata {
|
||||
standard: MetadataStandard::Simple,
|
||||
uri: "test_uri".to_string(),
|
||||
creators: "test_creators".to_string(),
|
||||
uri: "test_uri".to_owned(),
|
||||
creators: "test_creators".to_owned(),
|
||||
};
|
||||
let _post_states = new_definition_with_metadata(
|
||||
definition_account,
|
||||
@ -919,7 +924,7 @@ fn test_call_new_definition_metadata_with_init_definition() {
|
||||
|
||||
#[should_panic(expected = "Metadata target account must have default values")]
|
||||
#[test]
|
||||
fn test_call_new_definition_metadata_with_init_metadata() {
|
||||
fn call_new_definition_metadata_with_init_metadata() {
|
||||
let definition_account = AccountWithMetadata {
|
||||
account: Account::default(),
|
||||
is_authorized: true,
|
||||
@ -933,12 +938,12 @@ fn test_call_new_definition_metadata_with_init_metadata() {
|
||||
let metadata_account = AccountForTests::holding_account_same_definition_mint();
|
||||
let new_definition = NewTokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: 15u128,
|
||||
total_supply: 15_u128,
|
||||
};
|
||||
let metadata = NewTokenMetadata {
|
||||
standard: MetadataStandard::Simple,
|
||||
uri: "test_uri".to_string(),
|
||||
creators: "test_creators".to_string(),
|
||||
uri: "test_uri".to_owned(),
|
||||
creators: "test_creators".to_owned(),
|
||||
};
|
||||
let _post_states = new_definition_with_metadata(
|
||||
definition_account,
|
||||
@ -951,7 +956,7 @@ fn test_call_new_definition_metadata_with_init_metadata() {
|
||||
|
||||
#[should_panic(expected = "Holding target account must have default values")]
|
||||
#[test]
|
||||
fn test_call_new_definition_metadata_with_init_holding() {
|
||||
fn call_new_definition_metadata_with_init_holding() {
|
||||
let definition_account = AccountWithMetadata {
|
||||
account: Account::default(),
|
||||
is_authorized: true,
|
||||
@ -965,12 +970,12 @@ fn test_call_new_definition_metadata_with_init_holding() {
|
||||
let holding_account = AccountForTests::holding_account_same_definition_mint();
|
||||
let new_definition = NewTokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: 15u128,
|
||||
total_supply: 15_u128,
|
||||
};
|
||||
let metadata = NewTokenMetadata {
|
||||
standard: MetadataStandard::Simple,
|
||||
uri: "test_uri".to_string(),
|
||||
creators: "test_creators".to_string(),
|
||||
uri: "test_uri".to_owned(),
|
||||
creators: "test_creators".to_owned(),
|
||||
};
|
||||
let _post_states = new_definition_with_metadata(
|
||||
definition_account,
|
||||
@ -983,7 +988,7 @@ fn test_call_new_definition_metadata_with_init_holding() {
|
||||
|
||||
#[should_panic(expected = "Master NFT Account must be authorized")]
|
||||
#[test]
|
||||
fn test_print_nft_master_account_must_be_authorized() {
|
||||
fn print_nft_master_account_must_be_authorized() {
|
||||
let master_account = AccountForTests::holding_account_uninit();
|
||||
let printed_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = print_nft(master_account, printed_account);
|
||||
@ -991,7 +996,7 @@ fn test_print_nft_master_account_must_be_authorized() {
|
||||
|
||||
#[should_panic(expected = "Printed Account must be uninitialized")]
|
||||
#[test]
|
||||
fn test_print_nft_print_account_initialized() {
|
||||
fn print_nft_print_account_initialized() {
|
||||
let master_account = AccountForTests::holding_account_master_nft();
|
||||
let printed_account = AccountForTests::holding_account_init();
|
||||
let _post_states = print_nft(master_account, printed_account);
|
||||
@ -999,7 +1004,7 @@ fn test_print_nft_print_account_initialized() {
|
||||
|
||||
#[should_panic(expected = "Invalid Token Holding data")]
|
||||
#[test]
|
||||
fn test_print_nft_master_nft_invalid_token_holding() {
|
||||
fn print_nft_master_nft_invalid_token_holding() {
|
||||
let master_account = AccountForTests::definition_account_auth();
|
||||
let printed_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = print_nft(master_account, printed_account);
|
||||
@ -1007,7 +1012,7 @@ fn test_print_nft_master_nft_invalid_token_holding() {
|
||||
|
||||
#[should_panic(expected = "Invalid Token Holding provided as NFT Master Account")]
|
||||
#[test]
|
||||
fn test_print_nft_master_nft_not_nft_master_account() {
|
||||
fn print_nft_master_nft_not_nft_master_account() {
|
||||
let master_account = AccountForTests::holding_account_init();
|
||||
let printed_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = print_nft(master_account, printed_account);
|
||||
@ -1015,14 +1020,14 @@ fn test_print_nft_master_nft_not_nft_master_account() {
|
||||
|
||||
#[should_panic(expected = "Insufficient balance to print another NFT copy")]
|
||||
#[test]
|
||||
fn test_print_nft_master_nft_insufficient_balance() {
|
||||
fn print_nft_master_nft_insufficient_balance() {
|
||||
let master_account = AccountForTests::holding_account_master_nft_insufficient_balance();
|
||||
let printed_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = print_nft(master_account, printed_account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_print_nft_success() {
|
||||
fn print_nft_success() {
|
||||
let master_account = AccountForTests::holding_account_master_nft();
|
||||
let printed_account = AccountForTests::holding_account_uninit();
|
||||
let post_states = print_nft(master_account, printed_account);
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context as _, Result};
|
||||
use bedrock_client::BedrockClient;
|
||||
pub use common::block::Block;
|
||||
pub use logos_blockchain_core::mantle::{MantleTx, SignedMantleTx, ops::channel::MsgId};
|
||||
use logos_blockchain_core::mantle::{
|
||||
Op, OpProof, Transaction, TxHash, ledger,
|
||||
Op, OpProof, Transaction as _, TxHash, ledger,
|
||||
ops::channel::{ChannelId, inscribe::InscriptionOp},
|
||||
};
|
||||
pub use logos_blockchain_key_management_system_service::keys::Ed25519Key;
|
||||
@ -14,7 +14,7 @@ use crate::config::BedrockConfig;
|
||||
#[expect(async_fn_in_trait, reason = "We don't care about Send/Sync here")]
|
||||
pub trait BlockSettlementClientTrait: Clone {
|
||||
//// Create a new client.
|
||||
fn new(config: &BedrockConfig, bedrock_signing_key: Ed25519Key) -> Result<Self>;
|
||||
fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result<Self>;
|
||||
|
||||
/// Get the bedrock channel ID used by this client.
|
||||
fn bedrock_channel_id(&self) -> ChannelId;
|
||||
|
||||
@ -24,16 +24,13 @@ impl SequencerStore {
|
||||
/// ATTENTION: Will overwrite genesis block.
|
||||
pub fn open_db_with_genesis(
|
||||
location: &Path,
|
||||
genesis_block: Option<(&Block, MantleMsgId)>,
|
||||
genesis_block: &Block,
|
||||
genesis_msg_id: MantleMsgId,
|
||||
signing_key: nssa::PrivateKey,
|
||||
) -> Result<Self> {
|
||||
let tx_hash_to_block_map = if let Some((block, _msg_id)) = &genesis_block {
|
||||
block_to_transactions_map(block)
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
let tx_hash_to_block_map = block_to_transactions_map(genesis_block);
|
||||
|
||||
let dbio = RocksDBIO::open_or_create(location, genesis_block)?;
|
||||
let dbio = RocksDBIO::open_or_create(location, genesis_block, genesis_msg_id)?;
|
||||
|
||||
let genesis_id = dbio.get_meta_first_block_in_db()?;
|
||||
|
||||
@ -45,11 +42,6 @@ impl SequencerStore {
|
||||
})
|
||||
}
|
||||
|
||||
/// Reopening existing database
|
||||
pub fn open_db_restart(location: &Path, signing_key: nssa::PrivateKey) -> Result<Self> {
|
||||
SequencerStore::open_db_with_genesis(location, None, signing_key)
|
||||
}
|
||||
|
||||
pub fn get_block_at_id(&self, id: u64) -> Result<Block> {
|
||||
Ok(self.dbio.get_block(id)?)
|
||||
}
|
||||
@ -120,13 +112,15 @@ pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap<HashType, u64>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")]
|
||||
|
||||
use common::{block::HashableBlockData, test_utils::sequencer_sign_key_for_testing};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_transaction_by_hash() {
|
||||
fn get_transaction_by_hash() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let path = temp_dir.path();
|
||||
|
||||
@ -141,12 +135,9 @@ mod tests {
|
||||
|
||||
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
||||
// Start an empty node store
|
||||
let mut node_store = SequencerStore::open_db_with_genesis(
|
||||
path,
|
||||
Some((&genesis_block, [0; 32])),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
let mut node_store =
|
||||
SequencerStore::open_db_with_genesis(path, &genesis_block, [0; 32], signing_key)
|
||||
.unwrap();
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]);
|
||||
@ -163,7 +154,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_latest_block_meta_returns_genesis_meta_initially() {
|
||||
fn latest_block_meta_returns_genesis_meta_initially() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let path = temp_dir.path();
|
||||
|
||||
@ -179,12 +170,9 @@ mod tests {
|
||||
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
||||
let genesis_hash = genesis_block.header.hash;
|
||||
|
||||
let node_store = SequencerStore::open_db_with_genesis(
|
||||
path,
|
||||
Some((&genesis_block, [0; 32])),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
let node_store =
|
||||
SequencerStore::open_db_with_genesis(path, &genesis_block, [0; 32], signing_key)
|
||||
.unwrap();
|
||||
|
||||
// Verify that initially the latest block hash equals genesis hash
|
||||
let latest_meta = node_store.latest_block_meta().unwrap();
|
||||
@ -193,7 +181,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_latest_block_meta_updates_after_new_block() {
|
||||
fn latest_block_meta_updates_after_new_block() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let path = temp_dir.path();
|
||||
|
||||
@ -207,12 +195,9 @@ mod tests {
|
||||
};
|
||||
|
||||
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
||||
let mut node_store = SequencerStore::open_db_with_genesis(
|
||||
path,
|
||||
Some((&genesis_block, [0; 32])),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
let mut node_store =
|
||||
SequencerStore::open_db_with_genesis(path, &genesis_block, [0; 32], signing_key)
|
||||
.unwrap();
|
||||
|
||||
// Add a new block
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
@ -232,7 +217,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_block_finalized() {
|
||||
fn mark_block_finalized() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let path = temp_dir.path();
|
||||
|
||||
@ -246,12 +231,9 @@ mod tests {
|
||||
};
|
||||
|
||||
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
||||
let mut node_store = SequencerStore::open_db_with_genesis(
|
||||
path,
|
||||
Some((&genesis_block, [0; 32])),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
let mut node_store =
|
||||
SequencerStore::open_db_with_genesis(path, &genesis_block, [0; 32], signing_key)
|
||||
.unwrap();
|
||||
|
||||
// Add a new block with Pending status
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
|
||||
@ -13,6 +13,8 @@ use config::SequencerConfig;
|
||||
use log::{error, info, warn};
|
||||
use logos_blockchain_key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key};
|
||||
use mempool::{MemPool, MemPoolHandle};
|
||||
#[cfg(feature = "mock")]
|
||||
pub use mock::SequencerCoreWithMockClients;
|
||||
|
||||
use crate::{
|
||||
block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait, MsgId},
|
||||
@ -24,11 +26,9 @@ pub mod block_settlement_client;
|
||||
pub mod block_store;
|
||||
pub mod config;
|
||||
pub mod indexer_client;
|
||||
#[cfg(feature = "mock")]
|
||||
pub mod mock;
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
pub use mock::SequencerCoreWithMockClients;
|
||||
pub mod mock;
|
||||
|
||||
pub struct SequencerCore<
|
||||
BC: BlockSettlementClientTrait = BlockSettlementClient,
|
||||
@ -82,7 +82,8 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
// as fixing this issue may require actions non-native to program scope
|
||||
let store = SequencerStore::open_db_with_genesis(
|
||||
&config.home.join("rocksdb"),
|
||||
Some((&genesis_block, genesis_msg_id.into())),
|
||||
&genesis_block,
|
||||
genesis_msg_id.into(),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
@ -182,7 +183,10 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
) -> Result<(SignedMantleTx, MsgId)> {
|
||||
let now = Instant::now();
|
||||
|
||||
let new_block_height = self.chain_height + 1;
|
||||
let new_block_height = self
|
||||
.chain_height
|
||||
.checked_add(1)
|
||||
.with_context(|| format!("Max block height reached: {}", self.chain_height))?;
|
||||
|
||||
let mut valid_transactions = vec![];
|
||||
|
||||
@ -338,12 +342,14 @@ impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerCore<BC, I
|
||||
fn load_or_create_signing_key(path: &Path) -> Result<Ed25519Key> {
|
||||
if path.exists() {
|
||||
let key_bytes = std::fs::read(path)?;
|
||||
|
||||
let key_array: [u8; ED25519_SECRET_KEY_SIZE] = key_bytes
|
||||
.try_into()
|
||||
.map_err(|_| anyhow!("Found key with incorrect length"))?;
|
||||
.map_err(|_bytes| anyhow!("Found key with incorrect length"))?;
|
||||
|
||||
Ok(Ed25519Key::from_bytes(&key_array))
|
||||
} else {
|
||||
let mut key_bytes = [0u8; ED25519_SECRET_KEY_SIZE];
|
||||
let mut key_bytes = [0_u8; ED25519_SECRET_KEY_SIZE];
|
||||
rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut key_bytes);
|
||||
// Create parent directory if it doesn't exist
|
||||
if let Some(parent) = path.parent() {
|
||||
@ -354,11 +360,14 @@ fn load_or_create_signing_key(path: &Path) -> Result<Ed25519Key> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "mock"))]
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "mock")]
|
||||
mod tests {
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")]
|
||||
|
||||
use std::{pin::pin, str::FromStr as _, time::Duration};
|
||||
|
||||
use base58::ToBase58;
|
||||
use base58::ToBase58 as _;
|
||||
use bedrock_client::BackoffConfig;
|
||||
use common::{
|
||||
block::AccountInitialData, test_utils::sequencer_sign_key_for_testing,
|
||||
@ -381,7 +390,7 @@ mod tests {
|
||||
|
||||
SequencerConfig {
|
||||
home,
|
||||
override_rust_log: Some("info".to_string()),
|
||||
override_rust_log: Some("info".to_owned()),
|
||||
genesis_id: 1,
|
||||
is_genesis_random: false,
|
||||
max_num_tx_in_block: 10,
|
||||
@ -462,7 +471,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_start_from_config() {
|
||||
async fn start_from_config() {
|
||||
let config = setup_sequencer_config();
|
||||
let (sequencer, _mempool_handle) =
|
||||
SequencerCoreWithMockClients::start_from_config(config.clone()).await;
|
||||
@ -482,7 +491,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_start_different_intial_accounts_balances() {
|
||||
async fn start_different_intial_accounts_balances() {
|
||||
let acc1_account_id: Vec<u8> = vec![
|
||||
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
|
||||
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
|
||||
@ -523,7 +532,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_pre_check_pass() {
|
||||
fn transaction_pre_check_pass() {
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
let result = tx.transaction_stateless_check();
|
||||
|
||||
@ -531,7 +540,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_valid() {
|
||||
async fn transaction_pre_check_native_transfer_valid() {
|
||||
let (sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -548,7 +557,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_other_signature() {
|
||||
async fn transaction_pre_check_native_transfer_other_signature() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -573,7 +582,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_sent_too_much() {
|
||||
async fn transaction_pre_check_native_transfer_sent_too_much() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -600,7 +609,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transaction_execute_native_transfer() {
|
||||
async fn transaction_execute_native_transfer() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -622,7 +631,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_push_tx_into_mempool_blocks_until_mempool_is_full() {
|
||||
async fn push_tx_into_mempool_blocks_until_mempool_is_full() {
|
||||
let config = SequencerConfig {
|
||||
mempool_max_size: 1,
|
||||
..setup_sequencer_config()
|
||||
@ -649,7 +658,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_produce_new_block_with_mempool_transactions() {
|
||||
async fn produce_new_block_with_mempool_transactions() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
let genesis_height = sequencer.chain_height;
|
||||
|
||||
@ -662,7 +671,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_replay_transactions_are_rejected_in_the_same_block() {
|
||||
async fn replay_transactions_are_rejected_in_the_same_block() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -694,7 +703,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_replay_transactions_are_rejected_in_different_blocks() {
|
||||
async fn replay_transactions_are_rejected_in_different_blocks() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0].account_id;
|
||||
@ -730,7 +739,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_restart_from_storage() {
|
||||
async fn restart_from_storage() {
|
||||
let config = setup_sequencer_config();
|
||||
let acc1_account_id = config.initial_accounts[0].account_id;
|
||||
let acc2_account_id = config.initial_accounts[1].account_id;
|
||||
@ -782,7 +791,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_pending_blocks() {
|
||||
async fn get_pending_blocks() {
|
||||
let config = setup_sequencer_config();
|
||||
let (mut sequencer, _mempool_handle) =
|
||||
SequencerCoreWithMockClients::start_from_config(config).await;
|
||||
@ -799,7 +808,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_blocks() {
|
||||
async fn delete_blocks() {
|
||||
let config = setup_sequencer_config();
|
||||
let (mut sequencer, _mempool_handle) =
|
||||
SequencerCoreWithMockClients::start_from_config(config).await;
|
||||
@ -822,7 +831,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_produce_block_with_correct_prev_meta_after_restart() {
|
||||
async fn produce_block_with_correct_prev_meta_after_restart() {
|
||||
let config = setup_sequencer_config();
|
||||
let acc1_account_id = config.initial_accounts[0].account_id;
|
||||
let acc2_account_id = config.initial_accounts[1].account_id;
|
||||
@ -895,7 +904,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_start_from_config_uses_db_height_not_config_genesis() {
|
||||
async fn start_from_config_uses_db_height_not_config_genesis() {
|
||||
let mut config = setup_sequencer_config();
|
||||
let original_genesis_id = config.genesis_id;
|
||||
|
||||
|
||||
@ -19,10 +19,10 @@ pub struct MockBlockSettlementClient {
|
||||
}
|
||||
|
||||
impl BlockSettlementClientTrait for MockBlockSettlementClient {
|
||||
fn new(config: &BedrockConfig, bedrock_signing_key: Ed25519Key) -> Result<Self> {
|
||||
fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result<Self> {
|
||||
Ok(Self {
|
||||
bedrock_channel_id: config.channel_id,
|
||||
bedrock_signing_key,
|
||||
bedrock_signing_key: signing_key,
|
||||
})
|
||||
}
|
||||
|
||||
@ -46,10 +46,10 @@ pub struct MockBlockSettlementClientWithError {
|
||||
}
|
||||
|
||||
impl BlockSettlementClientTrait for MockBlockSettlementClientWithError {
|
||||
fn new(config: &BedrockConfig, bedrock_signing_key: Ed25519Key) -> Result<Self> {
|
||||
fn new(config: &BedrockConfig, signing_key: Ed25519Key) -> Result<Self> {
|
||||
Ok(Self {
|
||||
bedrock_channel_id: config.channel_id,
|
||||
bedrock_signing_key,
|
||||
bedrock_signing_key: signing_key,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,3 @@
|
||||
pub mod net_utils;
|
||||
pub mod process;
|
||||
pub mod types;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::{
|
||||
@ -10,6 +6,8 @@ use common::{
|
||||
};
|
||||
use mempool::MemPoolHandle;
|
||||
pub use net_utils::*;
|
||||
#[cfg(feature = "standalone")]
|
||||
use sequencer_core::mock::{MockBlockSettlementClient, MockIndexerClient};
|
||||
use sequencer_core::{
|
||||
SequencerCore,
|
||||
block_settlement_client::{BlockSettlementClient, BlockSettlementClientTrait},
|
||||
@ -21,6 +19,13 @@ use tokio::sync::Mutex;
|
||||
|
||||
use self::types::err_rpc::RpcErr;
|
||||
|
||||
pub mod net_utils;
|
||||
pub mod process;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(feature = "standalone")]
|
||||
pub type JsonHandlerWithMockClients = JsonHandler<MockBlockSettlementClient, MockIndexerClient>;
|
||||
|
||||
// ToDo: Add necessary fields
|
||||
pub struct JsonHandler<
|
||||
BC: BlockSettlementClientTrait = BlockSettlementClient,
|
||||
@ -51,9 +56,3 @@ pub fn rpc_error_responce_inverter(err: RpcError) -> RpcError {
|
||||
data: content,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "standalone")]
|
||||
use sequencer_core::mock::{MockBlockSettlementClient, MockIndexerClient};
|
||||
|
||||
#[cfg(feature = "standalone")]
|
||||
pub type JsonHandlerWithMockClients = JsonHandler<MockBlockSettlementClient, MockIndexerClient>;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user