mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-08 08:13:08 +00:00
feat: private token transfer
This commit is contained in:
parent
854d96af72
commit
eb9c45bbb7
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1058,6 +1058,7 @@ dependencies = [
|
||||
"k256",
|
||||
"log",
|
||||
"nssa",
|
||||
"nssa-core",
|
||||
"reqwest 0.11.27",
|
||||
"rs_merkle",
|
||||
"secp256k1-zkp",
|
||||
@ -3935,6 +3936,7 @@ dependencies = [
|
||||
"hex",
|
||||
"log",
|
||||
"nssa",
|
||||
"nssa-core",
|
||||
"sequencer_core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4680,6 +4682,7 @@ dependencies = [
|
||||
"clap",
|
||||
"common",
|
||||
"env_logger",
|
||||
"k256",
|
||||
"key_protocol",
|
||||
"log",
|
||||
"nssa",
|
||||
|
||||
@ -16,6 +16,7 @@ sha2.workspace = true
|
||||
log.workspace = true
|
||||
elliptic-curve.workspace = true
|
||||
hex.workspace = true
|
||||
nssa-core = { path = "../nssa/core", features = ["host"] }
|
||||
|
||||
[dependencies.secp256k1-zkp]
|
||||
workspace = true
|
||||
|
||||
@ -53,6 +53,11 @@ pub struct GetAccountDataRequest {
|
||||
pub address: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProofByCommitmentRequest {
|
||||
pub commitment: nssa_core::Commitment,
|
||||
}
|
||||
|
||||
parse_request!(HelloRequest);
|
||||
parse_request!(RegisterAccountRequest);
|
||||
parse_request!(SendTxRequest);
|
||||
@ -64,6 +69,7 @@ parse_request!(GetAccountBalanceRequest);
|
||||
parse_request!(GetTransactionByHashRequest);
|
||||
parse_request!(GetAccountsNoncesRequest);
|
||||
parse_request!(GetAccountDataRequest);
|
||||
parse_request!(GetProofByCommitmentRequest);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloResponse {
|
||||
@ -118,3 +124,8 @@ pub struct GetAccountDataResponse {
|
||||
pub program_owner: [u32; 8],
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetProofByCommitmentResponse {
|
||||
pub membership_proof: Option<nssa_core::MembershipProof>,
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@ use reqwest::Client;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::rpc_primitives::requests::{
|
||||
GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetProofByCommitmentRequest,
|
||||
GetProofByCommitmentResponse, GetTransactionByHashRequest, GetTransactionByHashResponse,
|
||||
};
|
||||
use crate::sequencer_client::json::AccountInitialData;
|
||||
use crate::transaction::{EncodedTransaction, NSSATransaction};
|
||||
@ -200,4 +200,25 @@ impl SequencerClient {
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get proof for commitment
|
||||
pub async fn get_proof_for_commitment(
|
||||
&self,
|
||||
commitment: nssa_core::Commitment,
|
||||
) -> Result<Option<nssa_core::MembershipProof>, SequencerClientError> {
|
||||
let acc_req = GetProofByCommitmentRequest { commitment };
|
||||
|
||||
let req = serde_json::to_value(acc_req).unwrap();
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_proof_for_commitment", req)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let resp_deser = serde_json::from_value::<GetProofByCommitmentResponse>(resp)
|
||||
.unwrap()
|
||||
.membership_proof;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
|
||||
}
|
||||
|
||||
pub async fn test_success() {
|
||||
let command = Command::SendNativeTokenTransfer {
|
||||
let command = Command::SendNativeTokenTransferPublic {
|
||||
from: ACC_SENDER.to_string(),
|
||||
to: ACC_RECEIVER.to_string(),
|
||||
amount: 100,
|
||||
@ -141,7 +141,7 @@ pub async fn test_success_move_to_another_account() {
|
||||
panic!("Failed to produce new account, not present in persistent accounts");
|
||||
}
|
||||
|
||||
let command = Command::SendNativeTokenTransfer {
|
||||
let command = Command::SendNativeTokenTransferPublic {
|
||||
from: ACC_SENDER.to_string(),
|
||||
to: new_persistent_account_addr.clone(),
|
||||
amount: 100,
|
||||
@ -172,7 +172,7 @@ pub async fn test_success_move_to_another_account() {
|
||||
}
|
||||
|
||||
pub async fn test_failure() {
|
||||
let command = Command::SendNativeTokenTransfer {
|
||||
let command = Command::SendNativeTokenTransferPublic {
|
||||
from: ACC_SENDER.to_string(),
|
||||
to: ACC_RECEIVER.to_string(),
|
||||
amount: 1000000,
|
||||
@ -209,7 +209,7 @@ pub async fn test_failure() {
|
||||
}
|
||||
|
||||
pub async fn test_success_two_transactions() {
|
||||
let command = Command::SendNativeTokenTransfer {
|
||||
let command = Command::SendNativeTokenTransferPublic {
|
||||
from: ACC_SENDER.to_string(),
|
||||
to: ACC_RECEIVER.to_string(),
|
||||
amount: 100,
|
||||
@ -242,7 +242,7 @@ pub async fn test_success_two_transactions() {
|
||||
|
||||
info!("First TX Success!");
|
||||
|
||||
let command = Command::SendNativeTokenTransfer {
|
||||
let command = Command::SendNativeTokenTransferPublic {
|
||||
from: ACC_SENDER.to_string(),
|
||||
to: ACC_RECEIVER.to_string(),
|
||||
amount: 100,
|
||||
|
||||
@ -37,9 +37,9 @@ impl EphemeralKeyHolder {
|
||||
|
||||
pub fn calculate_shared_secret_sender(
|
||||
&self,
|
||||
receiver_incoming_viewing_public_key: Scalar,
|
||||
) -> Scalar {
|
||||
receiver_incoming_viewing_public_key * self.ephemeral_secret_key
|
||||
receiver_incoming_viewing_public_key: AffinePoint,
|
||||
) -> AffinePoint {
|
||||
(receiver_incoming_viewing_public_key * self.ephemeral_secret_key).into()
|
||||
}
|
||||
|
||||
pub fn log(&self) {
|
||||
|
||||
@ -6,7 +6,7 @@ use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub(crate) mod shared_key_derivation;
|
||||
pub mod shared_key_derivation;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey};
|
||||
@ -14,7 +14,7 @@ pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, Incoming
|
||||
use crate::{Commitment, account::Account};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct SharedSecretKey([u8; 32]);
|
||||
pub struct SharedSecretKey(pub [u8; 32]);
|
||||
|
||||
pub struct EncryptionScheme;
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ use k256::{
|
||||
use crate::SharedSecretKey;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Secp256k1Point(pub(crate) Vec<u8>);
|
||||
pub struct Secp256k1Point(pub Vec<u8>);
|
||||
|
||||
impl Secp256k1Point {
|
||||
pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point {
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::Commitment;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, Hash))]
|
||||
pub struct NullifierPublicKey(pub(super) [u8; 32]);
|
||||
pub struct NullifierPublicKey(pub [u8; 32]);
|
||||
|
||||
impl From<&NullifierSecretKey> for NullifierPublicKey {
|
||||
fn from(value: &NullifierSecretKey) -> Self {
|
||||
|
||||
@ -12,6 +12,7 @@ actix-cors.workspace = true
|
||||
futures.workspace = true
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
nssa-core = { path = "../nssa/core", features = ["host"] }
|
||||
base64.workspace = true
|
||||
|
||||
actix-web.workspace = true
|
||||
|
||||
@ -14,7 +14,8 @@ use common::{
|
||||
requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountDataRequest,
|
||||
GetAccountDataResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||
GetInitialTestnetAccountsRequest, GetTransactionByHashRequest,
|
||||
GetInitialTestnetAccountsRequest, GetProofByCommitmentRequest,
|
||||
GetProofByCommitmentResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
},
|
||||
},
|
||||
@ -38,6 +39,7 @@ 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 GET_PROOF_FOR_COMMITMENT: &str = "get_proof_for_commitment";
|
||||
|
||||
pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
|
||||
|
||||
@ -255,6 +257,21 @@ impl JsonHandler {
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
/// Returns the commitment proof, corresponding to commitment
|
||||
async fn process_get_proof_by_commitment(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_proof_req = GetProofByCommitmentRequest::parse(Some(request.params))?;
|
||||
|
||||
let membership_proof = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
state
|
||||
.store
|
||||
.state
|
||||
.get_proof_for_commitment(&get_proof_req.commitment)
|
||||
};
|
||||
let helperstruct = GetProofByCommitmentResponse { membership_proof };
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
pub async fn process_request_internal(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
match request.method.as_ref() {
|
||||
HELLO => self.process_temp_hello(request).await,
|
||||
@ -267,6 +284,7 @@ impl JsonHandler {
|
||||
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,
|
||||
GET_PROOF_FOR_COMMITMENT => self.process_get_proof_by_commitment(request).await,
|
||||
_ => Err(RpcErr(RpcError::method_not_found(request.method))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ tempfile.workspace = true
|
||||
clap.workspace = true
|
||||
nssa-core = { path = "../nssa/core" }
|
||||
base64.workspace = true
|
||||
k256 = { version = "0.13.3" }
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
@ -10,6 +10,8 @@ use common::{
|
||||
use anyhow::Result;
|
||||
use chain_storage::WalletChainStore;
|
||||
use config::WalletConfig;
|
||||
use k256::elliptic_curve::group::GroupEncoding;
|
||||
use k256::elliptic_curve::sec1::ToEncodedPoint;
|
||||
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use log::info;
|
||||
use nssa::Address;
|
||||
@ -176,37 +178,86 @@ impl WalletCore {
|
||||
&[
|
||||
(
|
||||
nssa_core::NullifierPublicKey(from_keys.nullifer_public_key),
|
||||
shared_secret,
|
||||
nssa_core::SharedSecretKey(
|
||||
shared_secret.to_bytes().as_slice().try_into().unwrap(),
|
||||
),
|
||||
),
|
||||
(
|
||||
nssa_core::NullifierPublicKey(to_keys.nullifer_public_key),
|
||||
shared_secret,
|
||||
nssa_core::SharedSecretKey(
|
||||
shared_secret.to_bytes().as_slice().try_into().unwrap(),
|
||||
),
|
||||
),
|
||||
],
|
||||
&[(
|
||||
from_keys.private_key_holder.nullifier_secret_key,
|
||||
state.get_proof_for_commitment(&sender_commitment).unwrap(),
|
||||
self.sequencer_client
|
||||
.get_proof_for_commitment(sender_commitment)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
)],
|
||||
&program,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
(sender_keys.npk(), sender_keys.ivk(), epk_1),
|
||||
(recipient_keys.npk(), recipient_keys.ivk(), epk_2),
|
||||
],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
(
|
||||
nssa_core::NullifierPublicKey(from_keys.nullifer_public_key),
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
from_keys
|
||||
.incoming_viewing_public_key
|
||||
.to_encoded_point(true)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
eph_holder
|
||||
.generate_ephemeral_public_key()
|
||||
.to_encoded_point(true)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
),
|
||||
(
|
||||
nssa_core::NullifierPublicKey(to_keys.nullifer_public_key),
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
to_keys
|
||||
.incoming_viewing_public_key
|
||||
.to_encoded_point(true)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
eph_holder
|
||||
.generate_ephemeral_public_key()
|
||||
.to_encoded_point(true)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
),
|
||||
],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
let witness_set =
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
||||
&message,
|
||||
proof,
|
||||
&[],
|
||||
);
|
||||
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new(
|
||||
message,
|
||||
witness_set,
|
||||
);
|
||||
|
||||
Ok(self.sequencer_client.send_tx_public(tx).await?)
|
||||
Ok(self.sequencer_client.send_tx_private(tx).await?)
|
||||
} else {
|
||||
Err(ExecutionFailureKind::InsufficientFundsError)
|
||||
}
|
||||
@ -231,7 +282,7 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
///Poll transactions
|
||||
pub async fn poll_public_native_token_transfer(&self, hash: String) -> Result<NSSATransaction> {
|
||||
pub async fn poll_native_token_transfer(&self, hash: String) -> Result<NSSATransaction> {
|
||||
let transaction_encoded = self.poller.poll_tx(hash).await?;
|
||||
let tx_base64_decode =
|
||||
base64::engine::general_purpose::STANDARD.decode(transaction_encoded)?;
|
||||
@ -246,7 +297,23 @@ impl WalletCore {
|
||||
#[clap(about)]
|
||||
pub enum Command {
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
SendNativeTokenTransfer {
|
||||
///
|
||||
/// Public operation
|
||||
SendNativeTokenTransferPublic {
|
||||
///from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
///amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Private operation
|
||||
SendNativeTokenTransferPrivate {
|
||||
///from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
@ -292,7 +359,7 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
|
||||
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?;
|
||||
|
||||
match command {
|
||||
Command::SendNativeTokenTransfer { from, to, amount } => {
|
||||
Command::SendNativeTokenTransferPublic { from, to, amount } => {
|
||||
let from = produce_account_addr_from_hex(from)?;
|
||||
let to = produce_account_addr_from_hex(to)?;
|
||||
|
||||
@ -302,10 +369,22 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
|
||||
|
||||
info!("Results of tx send is {res:#?}");
|
||||
|
||||
let transfer_tx = wallet_core
|
||||
.poll_public_native_token_transfer(res.tx_hash)
|
||||
let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
|
||||
|
||||
info!("Transaction data is {transfer_tx:?}");
|
||||
}
|
||||
Command::SendNativeTokenTransferPrivate { from, to, amount } => {
|
||||
let from = produce_account_addr_from_hex(from)?;
|
||||
let to = produce_account_addr_from_hex(to)?;
|
||||
|
||||
let res = wallet_core
|
||||
.send_private_native_token_transfer(from, to, amount)
|
||||
.await?;
|
||||
|
||||
info!("Results of tx send is {res:#?}");
|
||||
|
||||
let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
|
||||
|
||||
info!("Transaction data is {transfer_tx:?}");
|
||||
}
|
||||
Command::RegisterAccountPublic {} => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user