mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'main' into Pravdyvy/sequencer-update
This commit is contained in:
commit
8ceb27421e
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
resolver = "3"
|
||||
members = [
|
||||
"integration_tests",
|
||||
"sequencer_runner",
|
||||
@ -25,7 +25,7 @@ futures = "0.3"
|
||||
actix-rt = "*"
|
||||
lazy_static = "1.5.0"
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
log = "0.4.28"
|
||||
lru = "0.7.8"
|
||||
thiserror = "2.0.12"
|
||||
rs_merkle = "1.4"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@ -10,7 +10,6 @@ serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
reqwest.workspace = true
|
||||
k256.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
rs_merkle.workspace = true
|
||||
sha2.workspace = true
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use serde_json::{to_value, Value};
|
||||
use serde_json::{Value, to_value};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
@ -65,7 +65,7 @@ impl RpcError {
|
||||
return Self::server_error(Some(format!(
|
||||
"Failed to serialize invalid parameters error: {:?}",
|
||||
err.to_string()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
RpcError::new(-32_602, "Invalid params".to_owned(), Some(value))
|
||||
@ -178,7 +178,7 @@ impl From<ServerError> for RpcError {
|
||||
let error_data = match to_value(&e) {
|
||||
Ok(value) => value,
|
||||
Err(_err) => {
|
||||
return RpcError::new_internal_error(None, "Failed to serialize ServerError")
|
||||
return RpcError::new_internal_error(None, "Failed to serialize ServerError");
|
||||
}
|
||||
};
|
||||
RpcError::new_internal_error(Some(error_data), e.to_string().as_str())
|
||||
|
||||
@ -315,10 +315,10 @@ impl From<Message> for Vec<u8> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::Value;
|
||||
use serde_json::de::from_slice;
|
||||
use serde_json::json;
|
||||
use serde_json::ser::to_vec;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::parse_request;
|
||||
|
||||
use super::errors::RpcParseError;
|
||||
use super::parser::parse_params;
|
||||
use super::parser::RpcRequest;
|
||||
use super::parser::parse_params;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
@ -48,6 +48,11 @@ pub struct GetAccountsNoncesRequest {
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountDataRequest {
|
||||
pub address: String,
|
||||
}
|
||||
|
||||
parse_request!(HelloRequest);
|
||||
parse_request!(RegisterAccountRequest);
|
||||
parse_request!(SendTxRequest);
|
||||
@ -58,6 +63,7 @@ parse_request!(GetInitialTestnetAccountsRequest);
|
||||
parse_request!(GetAccountBalanceRequest);
|
||||
parse_request!(GetTransactionByHashRequest);
|
||||
parse_request!(GetAccountsNoncesRequest);
|
||||
parse_request!(GetAccountDataRequest);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloResponse {
|
||||
@ -104,3 +110,11 @@ pub struct GetAccountsNoncesResponse {
|
||||
pub struct GetTransactionByHashResponse {
|
||||
pub transaction: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountDataResponse {
|
||||
pub balance: u128,
|
||||
pub nonce: u128,
|
||||
pub program_owner: [u32; 8],
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
use k256::ecdsa::{Signature, SigningKey, VerifyingKey};
|
||||
use k256::ecdsa::{
|
||||
Signature, SigningKey, VerifyingKey,
|
||||
signature::{Signer, Verifier},
|
||||
};
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use sha2::{digest::FixedOutput, Digest};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
use elliptic_curve::{
|
||||
consts::{B0, B1},
|
||||
@ -185,7 +188,10 @@ pub type SignaturePrivateKey = SigningKey;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sha2::{digest::FixedOutput, Digest};
|
||||
use super::*;
|
||||
use k256::{FieldBytes, ecdsa::signature::Signer};
|
||||
use secp256k1_zkp::{Tweak, constants::SECRET_KEY_SIZE};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
use crate::{
|
||||
transaction::{TransactionBody, TxKind},
|
||||
@ -222,5 +228,62 @@ mod tests {
|
||||
let body_new = TransactionBody::from_bytes(body_bytes);
|
||||
|
||||
assert_eq!(body, body_new);
|
||||
#[test]
|
||||
fn test_into_authenticated_succeeds_for_valid_signature() {
|
||||
let transaction = test_transaction();
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
|
||||
let signature = authenticated_tx.transaction().signature;
|
||||
let hash = authenticated_tx.hash();
|
||||
|
||||
assert_eq!(authenticated_tx.transaction(), &transaction);
|
||||
assert_eq!(hash, &transaction.body.hash());
|
||||
assert!(
|
||||
authenticated_tx
|
||||
.transaction()
|
||||
.public_key
|
||||
.verify(&transaction.body.to_bytes(), &signature)
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_authenticated_fails_for_invalid_signature() {
|
||||
let body = test_transaction_body();
|
||||
let key_bytes = FieldBytes::from_slice(&[37; 32]);
|
||||
let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap();
|
||||
let transaction = {
|
||||
let mut this = Transaction::new(body, private_key.clone());
|
||||
// Modify the signature to make it invalid
|
||||
// We do this by changing it to the signature of something else
|
||||
this.signature = private_key.sign(b"deadbeef");
|
||||
this
|
||||
};
|
||||
|
||||
matches!(
|
||||
transaction.into_authenticated(),
|
||||
Err(TransactionSignatureError::InvalidSignature)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authenticated_transaction_getter() {
|
||||
let transaction = test_transaction();
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
assert_eq!(authenticated_tx.transaction(), &transaction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authenticated_transaction_hash_getter() {
|
||||
let transaction = test_transaction();
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
assert_eq!(authenticated_tx.hash(), &transaction.body.hash());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authenticated_transaction_into_transaction() {
|
||||
let transaction = test_transaction();
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
assert_eq!(authenticated_tx.into_transaction(), transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,15 +5,12 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
actix.workspace = true
|
||||
|
||||
actix-web.workspace = true
|
||||
tokio.workspace = true
|
||||
toml.workspace = true
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
@ -21,9 +18,6 @@ tempfile.workspace = true
|
||||
features = ["derive", "env"]
|
||||
workspace = true
|
||||
|
||||
[dependencies.sequencer_rpc]
|
||||
path = "../sequencer_rpc"
|
||||
|
||||
[dependencies.sequencer_core]
|
||||
path = "../sequencer_core"
|
||||
|
||||
@ -35,6 +29,3 @@ path = "../wallet"
|
||||
|
||||
[dependencies.common]
|
||||
path = "../common"
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "key_protocol"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
k256.workspace = true
|
||||
@ -16,8 +15,6 @@ elliptic-curve.workspace = true
|
||||
hex.workspace = true
|
||||
aes-gcm.workspace = true
|
||||
lazy_static.workspace = true
|
||||
tiny-keccak.workspace = true
|
||||
nssa-core = { path = "../nssa/core" }
|
||||
|
||||
[dependencies.common]
|
||||
path = "../common"
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, KeyInit};
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use aes_gcm::{AeadCore, Aes256Gcm, KeyInit, aead::Aead};
|
||||
use elliptic_curve::PrimeField;
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::{AffinePoint, FieldBytes, Scalar};
|
||||
use log::info;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use super::constants_types::{CipherText, Nonce};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit};
|
||||
use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead};
|
||||
use constants_types::{CipherText, Nonce};
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::AffinePoint;
|
||||
@ -136,8 +136,8 @@ impl KeyChain {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use aes_gcm::{
|
||||
aead::{Aead, KeyInit, OsRng},
|
||||
Aes256Gcm,
|
||||
aead::{Aead, KeyInit, OsRng},
|
||||
};
|
||||
use constants_types::{CipherText, Nonce};
|
||||
use constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use common::TreeHashType;
|
||||
use elliptic_curve::PrimeField;
|
||||
use k256::{AffinePoint, FieldBytes, Scalar};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{digest::FixedOutput, Digest};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
||||
|
||||
|
||||
@ -36,7 +36,9 @@ impl NSSAUserData {
|
||||
accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
||||
) -> Result<Self> {
|
||||
if !Self::valid_key_transaction_pairing_check(&accounts_keys) {
|
||||
anyhow::bail!("Key transaction pairing check not satisfied, there is addresses, which is not derived from keys");
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys);
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
[package]
|
||||
name = "mempool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@ -13,7 +13,6 @@ sha2 = "0.10.9"
|
||||
secp256k1 = "0.31.1"
|
||||
rand = "0.8"
|
||||
borsh = "1.5.7"
|
||||
bytemuck = "1.13"
|
||||
hex = "0.4.3"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@ -8,6 +8,8 @@ use crate::{NullifierPublicKey, account::Account};
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
impl Commitment {
|
||||
/// Generates the commitment to a private account owned by user for npk:
|
||||
/// SHA256(npk || program_owner || balance || nonce || data)
|
||||
pub fn new(npk: &NullifierPublicKey, account: &Account) -> Self {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
@ -34,6 +36,7 @@ pub type CommitmentSetDigest = [u8; 32];
|
||||
|
||||
pub type MembershipProof = (usize, Vec<[u8; 32]>);
|
||||
|
||||
/// Computes the resulting digest for the given membership proof and corresponding commitment
|
||||
pub fn compute_digest_for_path(
|
||||
commitment: &Commitment,
|
||||
proof: &MembershipProof,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "program-methods"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "3.0.3" }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "programs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
|
||||
|
||||
@ -39,11 +39,11 @@ impl MerkleTree {
|
||||
if tree_depth == capacity_depth {
|
||||
0
|
||||
} else {
|
||||
// 2^(capacity_depth - tree_depth) - 1
|
||||
(1 << (capacity_depth - tree_depth)) - 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of levels required to hold all values
|
||||
/// Number of levels required to hold all nodes
|
||||
fn depth(&self) -> usize {
|
||||
self.length.next_power_of_two().trailing_zeros() as usize
|
||||
}
|
||||
@ -57,6 +57,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;
|
||||
|
||||
@ -75,6 +76,8 @@ impl MerkleTree {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reallocates storage of Merkle tree for double capacity.
|
||||
/// The current tree is embedded into the new tree as a subtree
|
||||
fn reallocate_to_double_capacity(&mut self) {
|
||||
let old_capacity = self.capacity;
|
||||
let new_capacity = old_capacity << 1;
|
||||
@ -102,9 +105,11 @@ impl MerkleTree {
|
||||
let mut node_index = new_index + self.capacity - 1;
|
||||
let mut node_hash = hash_value(&value);
|
||||
|
||||
// Insert the new node at the bottom layer
|
||||
self.set_node(node_index, node_hash);
|
||||
self.length += 1;
|
||||
|
||||
// Update upper levels for the newly inserted node
|
||||
for _ in 0..self.depth() {
|
||||
let parent_index = (node_index - 1) >> 1;
|
||||
let left_child = self.get_node((parent_index << 1) + 1);
|
||||
@ -129,6 +134,7 @@ impl MerkleTree {
|
||||
|
||||
while node_index != root_index {
|
||||
let parent_index = (node_index - 1) >> 1;
|
||||
// Left children have odd indices, and right children have even indices
|
||||
let is_left_child = node_index & 1 == 1;
|
||||
let sibling_index = if is_left_child {
|
||||
node_index + 1
|
||||
|
||||
@ -21,6 +21,7 @@ impl CommitmentSet {
|
||||
self.merkle_tree.root()
|
||||
}
|
||||
|
||||
/// Queries the `CommitmentSet` for a membership proof of commitment
|
||||
pub fn get_proof_for(&self, commitment: &Commitment) -> Option<MembershipProof> {
|
||||
let index = *self.commitments.get(commitment)?;
|
||||
|
||||
@ -29,6 +30,7 @@ impl CommitmentSet {
|
||||
.map(|path| (index, path))
|
||||
}
|
||||
|
||||
/// Inserts a list of commitments to the `CommitmentSet`.
|
||||
pub(crate) fn extend(&mut self, commitments: &[Commitment]) {
|
||||
for commitment in commitments.iter().cloned() {
|
||||
let index = self.merkle_tree.insert(commitment.to_byte_array());
|
||||
@ -41,6 +43,9 @@ impl CommitmentSet {
|
||||
self.commitments.contains_key(commitment)
|
||||
}
|
||||
|
||||
/// Initializes an empty `CommitmentSet` with a given capacity.
|
||||
/// If the capacity is not a power_of_two, then capacity is taken
|
||||
/// to be the next power_of_two.
|
||||
pub(crate) fn with_capacity(capacity: usize) -> CommitmentSet {
|
||||
Self {
|
||||
merkle_tree: MerkleTree::with_capacity(capacity),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "test-program-methods"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "3.0.3" }
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
[package]
|
||||
name = "programs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "3.0.3", default-features = false, features = ['std'] }
|
||||
nssa-core = { path = "../../core" }
|
||||
|
||||
@ -1 +1 @@
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
[package]
|
||||
name = "sequencer_core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
hex.workspace = true
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
rand.workspace = true
|
||||
elliptic-curve.workspace = true
|
||||
k256.workspace = true
|
||||
tiny-keccak.workspace = true
|
||||
tempfile.workspace = true
|
||||
chrono.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
[dependencies.storage]
|
||||
path = "../storage"
|
||||
@ -23,15 +18,8 @@ path = "../storage"
|
||||
[dependencies.mempool]
|
||||
path = "../mempool"
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
[dependencies.common]
|
||||
path = "../common"
|
||||
|
||||
[dependencies.nssa]
|
||||
path = "../nssa"
|
||||
|
||||
[dependencies.secp256k1-zkp]
|
||||
workspace = true
|
||||
features = ["std", "rand-std", "rand", "serde", "global-context"]
|
||||
|
||||
@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use block_store::SequecerBlockStore;
|
||||
use common::block::HashableBlockData;
|
||||
use nssa::{self, Address};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use crate::config::AccountInitialData;
|
||||
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
[package]
|
||||
name = "sequencer_rpc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
actix.workspace = true
|
||||
actix-cors.workspace = true
|
||||
futures.workspace = true
|
||||
hex.workspace = true
|
||||
@ -19,18 +17,9 @@ base64.workspace = true
|
||||
actix-web.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
[dependencies.mempool]
|
||||
path = "../mempool"
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
[dependencies.sequencer_core]
|
||||
path = "../sequencer_core"
|
||||
|
||||
[dependencies.storage]
|
||||
path = "../storage"
|
||||
|
||||
[dependencies.common]
|
||||
path = "../common"
|
||||
|
||||
|
||||
@ -5,8 +5,8 @@ pub mod types;
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::rpc_primitives::{
|
||||
errors::{RpcError, RpcErrorKind},
|
||||
RpcPollingConfig,
|
||||
errors::{RpcError, RpcErrorKind},
|
||||
};
|
||||
use sequencer_core::SequencerCore;
|
||||
use serde::Serialize;
|
||||
|
||||
@ -2,13 +2,13 @@ use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer};
|
||||
use actix_web::{App, Error as HttpError, HttpResponse, HttpServer, http, middleware, web};
|
||||
use futures::Future;
|
||||
use futures::FutureExt;
|
||||
use log::info;
|
||||
|
||||
use common::rpc_primitives::message::Message;
|
||||
use common::rpc_primitives::RpcConfig;
|
||||
use common::rpc_primitives::message::Message;
|
||||
use sequencer_core::SequencerCore;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use actix_web::Error as HttpError;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nssa;
|
||||
use sequencer_core::config::AccountInitialData;
|
||||
use serde_json::Value;
|
||||
@ -11,9 +11,10 @@ use common::{
|
||||
message::{Message, Request},
|
||||
parser::RpcRequest,
|
||||
requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountsNoncesRequest,
|
||||
GetAccountsNoncesResponse, GetInitialTestnetAccountsRequest,
|
||||
GetTransactionByHashRequest, GetTransactionByHashResponse,
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountDataRequest,
|
||||
GetAccountDataResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||
GetInitialTestnetAccountsRequest, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
},
|
||||
},
|
||||
transaction::TransactionBody,
|
||||
@ -26,7 +27,7 @@ use common::rpc_primitives::requests::{
|
||||
SendTxResponse,
|
||||
};
|
||||
|
||||
use super::{respond, types::err_rpc::RpcErr, JsonHandler};
|
||||
use super::{JsonHandler, respond, types::err_rpc::RpcErr};
|
||||
|
||||
pub const HELLO: &str = "hello";
|
||||
pub const SEND_TX: &str = "send_tx";
|
||||
@ -36,6 +37,7 @@ pub const GET_LAST_BLOCK: &str = "get_last_block";
|
||||
pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance";
|
||||
pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash";
|
||||
pub const GET_ACCOUNTS_NONCES: &str = "get_accounts_nonces";
|
||||
pub const GET_ACCOUNT_DATA: &str = "get_account_data";
|
||||
|
||||
pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
|
||||
|
||||
@ -181,14 +183,9 @@ impl JsonHandler {
|
||||
let get_account_nonces_req = GetAccountsNoncesRequest::parse(Some(request.params))?;
|
||||
let mut addresses = vec![];
|
||||
for address_raw in get_account_nonces_req.addresses {
|
||||
let address_bytes = hex::decode(address_raw)
|
||||
.map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?;
|
||||
|
||||
let address = nssa::Address::new(
|
||||
address_bytes
|
||||
.try_into()
|
||||
.map_err(|_| RpcError::invalid_params("invalid length".to_string()))?,
|
||||
);
|
||||
let address = address_raw
|
||||
.parse::<nssa::Address>()
|
||||
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
|
||||
|
||||
addresses.push(address);
|
||||
}
|
||||
@ -207,6 +204,32 @@ impl JsonHandler {
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
///Returns account struct for give address.
|
||||
/// Address must be a valid hex string of the correct length.
|
||||
async fn process_get_account_data(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_account_nonces_req = GetAccountDataRequest::parse(Some(request.params))?;
|
||||
|
||||
let address = get_account_nonces_req
|
||||
.address
|
||||
.parse::<nssa::Address>()
|
||||
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
|
||||
|
||||
let account = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.store.state.get_account_by_address(&address)
|
||||
};
|
||||
|
||||
let helperstruct = GetAccountDataResponse {
|
||||
balance: account.balance,
|
||||
nonce: account.nonce,
|
||||
program_owner: account.program_owner,
|
||||
data: account.data,
|
||||
};
|
||||
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
/// Returns the transaction corresponding to the given hash, if it exists in the blockchain.
|
||||
/// The hash must be a valid hex string of the correct length.
|
||||
async fn process_get_transaction_by_hash(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -242,6 +265,7 @@ impl JsonHandler {
|
||||
GET_INITIAL_TESTNET_ACCOUNTS => self.get_initial_testnet_accounts(request).await,
|
||||
GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await,
|
||||
GET_ACCOUNTS_NONCES => self.process_get_accounts_nonces(request).await,
|
||||
GET_ACCOUNT_DATA => self.process_get_account_data(request).await,
|
||||
GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await,
|
||||
_ => Err(RpcErr(RpcError::method_not_found(request.method))),
|
||||
}
|
||||
@ -260,8 +284,8 @@ mod tests {
|
||||
};
|
||||
|
||||
use sequencer_core::{
|
||||
config::{AccountInitialData, SequencerConfig},
|
||||
SequencerCore,
|
||||
config::{AccountInitialData, SequencerConfig},
|
||||
};
|
||||
use serde_json::Value;
|
||||
use tempfile::tempdir;
|
||||
@ -344,7 +368,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn call_rpc_handler_with_json(handler: JsonHandler, request_json: Value) -> Value {
|
||||
use actix_web::{test, web, App};
|
||||
use actix_web::{App, test, web};
|
||||
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
@ -505,6 +529,31 @@ mod tests {
|
||||
assert_eq!(response, expected_response);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_data_for_non_existent_account() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account_data",
|
||||
"params": { "address": "efac".repeat(16) },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"balance": 0,
|
||||
"nonce": 0,
|
||||
"program_owner": [ 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"data": [],
|
||||
}
|
||||
});
|
||||
|
||||
let response = call_rpc_handler_with_json(json_handler, request).await;
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_transaction_by_hash_for_non_existent_hash() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
|
||||
@ -1,27 +1,22 @@
|
||||
[package]
|
||||
name = "sequencer_runner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
actix.workspace = true
|
||||
|
||||
actix-web.workspace = true
|
||||
tokio.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
[dependencies.clap]
|
||||
features = ["derive", "env"]
|
||||
workspace = true
|
||||
|
||||
[dependencies.mempool]
|
||||
path = "../mempool"
|
||||
|
||||
[dependencies.sequencer_rpc]
|
||||
path = "../sequencer_rpc"
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use common::rpc_primitives::RpcConfig;
|
||||
use log::info;
|
||||
use sequencer_core::{config::SequencerConfig, SequencerCore};
|
||||
use sequencer_core::{SequencerCore, config::SequencerConfig};
|
||||
use sequencer_rpc::new_http_server;
|
||||
use tokio::{sync::Mutex, task::JoinHandle};
|
||||
|
||||
@ -71,7 +71,9 @@ pub async fn main_runner() -> Result<()> {
|
||||
if let Some(ref rust_log) = app_config.override_rust_log {
|
||||
info!("RUST_LOG env var set to {rust_log:?}");
|
||||
|
||||
std::env::set_var(RUST_LOG, rust_log);
|
||||
unsafe {
|
||||
std::env::set_var(RUST_LOG, rust_log);
|
||||
}
|
||||
}
|
||||
|
||||
//ToDo: Add restart on failures
|
||||
|
||||
@ -1,17 +1,13 @@
|
||||
[package]
|
||||
name = "storage"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
lru.workspace = true
|
||||
thiserror.workspace = true
|
||||
hex.workspace = true
|
||||
|
||||
rocksdb.workspace = true
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use error::DbError;
|
||||
use rocksdb::{
|
||||
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options,
|
||||
};
|
||||
use sc_db_utils::{produce_blob_from_fit_vec, DataBlob, DataBlobChangeVariant};
|
||||
use sc_db_utils::{DataBlob, DataBlobChangeVariant, produce_blob_from_fit_vec};
|
||||
|
||||
pub mod error;
|
||||
pub mod sc_db_utils;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use serde::{de::Error, Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize, de::Error};
|
||||
|
||||
use crate::SC_DATA_BLOB_SIZE;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "wallet"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@ -9,18 +9,8 @@ serde_json.workspace = true
|
||||
env_logger.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
rand.workspace = true
|
||||
k256.workspace = true
|
||||
sha2.workspace = true
|
||||
bincode.workspace = true
|
||||
elliptic-curve.workspace = true
|
||||
reqwest.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tempfile.workspace = true
|
||||
risc0-zkvm = "3.0.3"
|
||||
hex.workspace = true
|
||||
actix-rt.workspace = true
|
||||
clap.workspace = true
|
||||
nssa-core = { path = "../nssa/core" }
|
||||
base64.workspace = true
|
||||
@ -33,7 +23,3 @@ path = "../nssa"
|
||||
|
||||
[dependencies.common]
|
||||
path = "../common"
|
||||
|
||||
[dependencies.secp256k1-zkp]
|
||||
workspace = true
|
||||
features = ["std", "rand-std", "rand", "serde", "global-context"]
|
||||
|
||||
@ -5,8 +5,8 @@ use key_protocol::key_protocol_core::NSSAUserData;
|
||||
use nssa::Address;
|
||||
|
||||
use crate::{
|
||||
config::{PersistentAccountData, WalletConfig},
|
||||
HOME_DIR_ENV_VAR,
|
||||
config::{PersistentAccountData, WalletConfig},
|
||||
};
|
||||
|
||||
///Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed.
|
||||
@ -69,12 +69,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_home_get_env_var() {
|
||||
std::env::set_var(HOME_DIR_ENV_VAR, "/path/to/configs");
|
||||
unsafe {
|
||||
std::env::set_var(HOME_DIR_ENV_VAR, "/path/to/configs");
|
||||
}
|
||||
|
||||
let home = get_home().unwrap();
|
||||
|
||||
assert_eq!(PathBuf::from_str("/path/to/configs").unwrap(), home);
|
||||
|
||||
std::env::remove_var(HOME_DIR_ENV_VAR);
|
||||
unsafe {
|
||||
std::env::remove_var(HOME_DIR_ENV_VAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use common::{
|
||||
sequencer_client::{json::SendTxResponse, SequencerClient},
|
||||
transaction::TransactionBody,
|
||||
ExecutionFailureKind,
|
||||
sequencer_client::{SequencerClient, json::SendTxResponse},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
@ -40,13 +41,7 @@ pub struct WalletCore {
|
||||
impl WalletCore {
|
||||
pub fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
|
||||
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
||||
let tx_poller = TxPoller {
|
||||
polling_delay_millis: config.seq_poll_timeout_millis,
|
||||
polling_max_blocks_to_query: config.seq_poll_max_blocks,
|
||||
polling_max_error_attempts: config.seq_poll_max_retries,
|
||||
polling_error_delay_millis: config.seq_poll_retry_delay_millis,
|
||||
client: client.clone(),
|
||||
};
|
||||
let tx_poller = TxPoller::new(config.clone(), client.clone());
|
||||
|
||||
let mut storage = WalletChainStore::new(config)?;
|
||||
|
||||
@ -62,7 +57,7 @@ impl WalletCore {
|
||||
})
|
||||
}
|
||||
|
||||
///Stre persistent accounts at home
|
||||
///Store persistent accounts at home
|
||||
pub fn store_persistent_accounts(&self) -> Result<PathBuf> {
|
||||
let home = get_home()?;
|
||||
let accs_path = home.join("curr_accounts.json");
|
||||
@ -97,40 +92,39 @@ impl WalletCore {
|
||||
to: Address,
|
||||
balance_to_move: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
if let Ok(balance) = self.get_account_balance(from).await {
|
||||
if balance >= balance_to_move {
|
||||
if let Ok(nonces) = self.get_accounts_nonces(vec![from]).await {
|
||||
let addresses = vec![from, to];
|
||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
addresses,
|
||||
nonces,
|
||||
balance_to_move,
|
||||
)
|
||||
.unwrap();
|
||||
let signing_key = self.storage.user_data.get_account_signing_key(&from);
|
||||
let Ok(balance) = self.get_account_balance(from).await else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
|
||||
if let Some(signing_key) = signing_key {
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(
|
||||
&message,
|
||||
&[signing_key],
|
||||
);
|
||||
if balance >= balance_to_move {
|
||||
let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
let addresses = vec![from, to];
|
||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
addresses,
|
||||
nonces,
|
||||
balance_to_move,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(self.sequencer_client.send_tx_public(tx).await?)
|
||||
} else {
|
||||
Err(ExecutionFailureKind::KeyNotFoundError)
|
||||
}
|
||||
} else {
|
||||
Err(ExecutionFailureKind::SequencerError)
|
||||
}
|
||||
} else {
|
||||
Err(ExecutionFailureKind::InsufficientFundsError)
|
||||
}
|
||||
let signing_key = self.storage.user_data.get_account_signing_key(&from);
|
||||
|
||||
let Some(signing_key) = signing_key else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
|
||||
let witness_set =
|
||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self.sequencer_client.send_tx(tx).await?)
|
||||
} else {
|
||||
Err(ExecutionFailureKind::SequencerError)
|
||||
Err(ExecutionFailureKind::InsufficientFundsError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use tokio::runtime::Builder;
|
||||
use wallet::{execute_subcommand, Args};
|
||||
use wallet::{Args, execute_subcommand};
|
||||
|
||||
pub const NUM_THREADS: usize = 2;
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ use anyhow::Result;
|
||||
use common::sequencer_client::SequencerClient;
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::config::WalletConfig;
|
||||
|
||||
#[derive(Clone)]
|
||||
///Helperstruct to poll transactions
|
||||
pub struct TxPoller {
|
||||
@ -15,6 +17,16 @@ pub struct TxPoller {
|
||||
}
|
||||
|
||||
impl TxPoller {
|
||||
pub fn new(config: WalletConfig, client: Arc<SequencerClient>) -> Self {
|
||||
Self {
|
||||
polling_delay_millis: config.seq_poll_timeout_millis,
|
||||
polling_max_blocks_to_query: config.seq_poll_max_blocks,
|
||||
polling_max_error_attempts: config.seq_poll_max_retries,
|
||||
polling_error_delay_millis: config.seq_poll_retry_delay_millis,
|
||||
client: client.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn poll_tx(&self, tx_hash: String) -> Result<String> {
|
||||
let max_blocks_to_query = self.polling_max_blocks_to_query;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user