Skip to content

Commit

Permalink
Merge branch 'main' into feature/crypto-wasm-compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
koxu1996 authored Apr 29, 2024
2 parents 6a19196 + e9ced86 commit 2a1996c
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 78 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 18 additions & 10 deletions kairos-server/tests/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use kairos_server::{
routes::{deposit::DepositPath, transfer::TransferPath, withdraw::WithdrawPath, PayloadBody},
state::BatchState,
};
use kairos_tx::helpers::{make_deposit, make_transfer, make_withdrawal};
use kairos_tx::asn::SigningPayload;
use tracing_subscriber::{prelude::*, EnvFilter};

static TEST_ENVIRONMENT: OnceLock<()> = OnceLock::new();
Expand All @@ -27,11 +27,10 @@ fn new_test_app() -> TestServer {
async fn test_deposit_withdraw() {
let server = new_test_app();

let nonce: u64 = 1;
let amount: u64 = 100;
let deposit = PayloadBody {
public_key: "alice_key".into(),
payload: make_deposit(nonce, amount).unwrap(),
payload: SigningPayload::new_deposit(amount).der_encode().unwrap(),
signature: vec![],
};

Expand Down Expand Up @@ -59,7 +58,9 @@ async fn test_deposit_withdraw() {
let amount: u64 = 50;
let withdrawal = PayloadBody {
public_key: "alice_key".into(),
payload: make_withdrawal(nonce, amount).unwrap(),
payload: SigningPayload::new_withdrawal(nonce, amount)
.der_encode()
.unwrap(),
signature: vec![],
};

Expand All @@ -74,7 +75,9 @@ async fn test_deposit_withdraw() {
let amount: u64 = 51;
let withdrawal = PayloadBody {
public_key: "alice_key".into(),
payload: make_withdrawal(nonce, amount).unwrap(),
payload: SigningPayload::new_withdrawal(nonce, amount)
.der_encode()
.unwrap(),
signature: vec![],
};

Expand All @@ -89,7 +92,9 @@ async fn test_deposit_withdraw() {
let amount: u64 = 50;
let withdrawal = PayloadBody {
public_key: "alice_key".into(),
payload: make_withdrawal(nonce, amount).unwrap(),
payload: SigningPayload::new_withdrawal(nonce, amount)
.der_encode()
.unwrap(),
signature: vec![],
};

Expand All @@ -111,11 +116,10 @@ async fn test_deposit_withdraw() {
async fn test_deposit_transfer_withdraw() {
let server = new_test_app();

let nonce: u64 = 1;
let amount: u64 = 100;
let deposit = PayloadBody {
public_key: "alice_key".into(),
payload: make_deposit(nonce, amount).unwrap(),
payload: SigningPayload::new_deposit(amount).der_encode().unwrap(),
signature: vec![],
};

Expand All @@ -124,15 +128,19 @@ async fn test_deposit_transfer_withdraw() {
let recipient: &[u8] = "bob_key".as_bytes();
let transfer = PayloadBody {
public_key: "alice_key".into(),
payload: make_transfer(nonce, recipient, amount).unwrap(),
payload: SigningPayload::new_transfer(nonce, recipient, amount)
.der_encode()
.unwrap(),
signature: vec![],
};

let nonce: u64 = 1;
let amount: u64 = 50;
let withdrawal = PayloadBody {
public_key: "bob_key".into(),
payload: make_withdrawal(nonce, amount).unwrap(),
payload: SigningPayload::new_withdrawal(nonce, amount)
.der_encode()
.unwrap(),
signature: vec![],
};

Expand Down
3 changes: 3 additions & 0 deletions kairos-tx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ license.workspace = true
num-traits = "0.2"
rasn = { version = "0.12", default-features = false, features = ["macros"] }

[dev-dependencies]
hex = "0.4"

[features]
default = ["std"]
std = []
170 changes: 159 additions & 11 deletions kairos-tx/src/asn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use num_traits::cast::ToPrimitive;
use rasn::types::AsnType;
use rasn::{Decode, Encode};

#[derive(AsnType, Encode, Decode, Debug)]
#[derive(AsnType, Encode, Decode, Debug, Clone)]
#[rasn(delegate)]
pub struct PublicKey(pub(crate) OctetString);

Expand All @@ -21,7 +21,19 @@ impl From<PublicKey> for Vec<u8> {
}
}

#[derive(AsnType, Encode, Decode, Debug)]
impl From<&[u8]> for PublicKey {
fn from(value: &[u8]) -> Self {
PublicKey(OctetString::copy_from_slice(value))
}
}

impl<const N: usize> From<&[u8; N]> for PublicKey {
fn from(value: &[u8; N]) -> Self {
PublicKey(OctetString::copy_from_slice(value))
}
}

#[derive(AsnType, Encode, Decode, Debug, Clone)]
#[rasn(delegate)]
pub struct Amount(pub(crate) Integer);

Expand All @@ -40,7 +52,13 @@ impl TryFrom<Amount> for u64 {
}
}

#[derive(AsnType, Encode, Decode, Debug)]
impl From<u64> for Amount {
fn from(value: u64) -> Self {
Amount(Integer::from(value))
}
}

#[derive(AsnType, Encode, Decode, Debug, Clone)]
#[rasn(delegate)]
pub struct Nonce(pub(crate) Integer);

Expand All @@ -58,13 +76,62 @@ impl TryFrom<Nonce> for u64 {
}
}

impl From<u64> for Nonce {
fn from(value: u64) -> Self {
Nonce(Integer::from(value))
}
}

#[derive(AsnType, Encode, Decode, Debug)]
#[non_exhaustive]
pub struct SigningPayload {
pub nonce: Nonce,
pub body: TransactionBody,
}

impl SigningPayload {
pub fn new(nonce: impl Into<Nonce>, body: impl Into<TransactionBody>) -> Self {
Self {
nonce: nonce.into(),
body: body.into(),
}
}

pub fn new_deposit(amount: impl Into<Amount>) -> Self {
Self {
// deposits have no meaningful nonce
nonce: 0.into(),
body: TransactionBody::Deposit(Deposit::new(amount)),
}
}

pub fn new_transfer(
nonce: impl Into<Nonce>,
recipient: impl Into<PublicKey>,
amount: impl Into<Amount>,
) -> Self {
Self {
nonce: nonce.into(),
body: TransactionBody::Transfer(Transfer::new(recipient, amount)),
}
}

pub fn new_withdrawal(nonce: impl Into<Nonce>, amount: impl Into<Amount>) -> Self {
Self {
nonce: nonce.into(),
body: TransactionBody::Withdrawal(Withdrawal::new(amount)),
}
}

pub fn der_encode(&self) -> Result<Vec<u8>, TxError> {
rasn::der::encode(self).map_err(TxError::EncodeError)
}

pub fn der_decode(value: impl AsRef<[u8]>) -> Result<Self, TxError> {
rasn::der::decode(value.as_ref()).map_err(TxError::DecodeError)
}
}

#[derive(AsnType, Encode, Decode, Debug)]
#[rasn(choice)]
#[non_exhaustive]
Expand All @@ -77,50 +144,92 @@ pub enum TransactionBody {
Withdrawal(Withdrawal),
}

impl From<Deposit> for TransactionBody {
fn from(value: Deposit) -> Self {
TransactionBody::Deposit(value)
}
}

impl From<Transfer> for TransactionBody {
fn from(value: Transfer) -> Self {
TransactionBody::Transfer(value)
}
}

impl From<Withdrawal> for TransactionBody {
fn from(value: Withdrawal) -> Self {
TransactionBody::Withdrawal(value)
}
}

#[derive(AsnType, Encode, Decode, Debug)]
#[non_exhaustive]
pub struct Deposit {
pub amount: Amount,
}

impl Deposit {
pub fn new(amount: impl Into<Amount>) -> Self {
Self {
amount: amount.into(),
}
}
}

#[derive(AsnType, Encode, Decode, Debug)]
#[non_exhaustive]
pub struct Transfer {
pub recipient: PublicKey,
pub amount: Amount,
}

impl Transfer {
pub fn new(recipient: impl Into<PublicKey>, amount: impl Into<Amount>) -> Self {
Self {
recipient: recipient.into(),
amount: amount.into(),
}
}
}

#[derive(AsnType, Encode, Decode, Debug)]
#[non_exhaustive]
pub struct Withdrawal {
pub amount: Amount,
}

impl Withdrawal {
pub fn new(amount: impl Into<Amount>) -> Self {
Self {
amount: amount.into(),
}
}
}

impl TryFrom<&[u8]> for SigningPayload {
type Error = TxError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
rasn::der::decode(value).map_err(TxError::DecodeError)
SigningPayload::der_decode(value)
}
}

impl TryFrom<SigningPayload> for Vec<u8> {
type Error = TxError;

fn try_from(value: SigningPayload) -> Result<Self, Self::Error> {
rasn::der::encode(&value).map_err(TxError::EncodeError)
value.der_encode()
}
}

#[cfg(test)]
mod tests {
use crate::helpers::{make_deposit, make_transfer, make_withdrawal};
use crate::asn::{Deposit, SigningPayload};

#[test]
fn test_encode_deposit() {
const NONCE: u64 = 1;
const AMOUNT: u64 = 1000;
let encoded = make_deposit(NONCE, AMOUNT).unwrap();
let encoded = SigningPayload::new_deposit(AMOUNT).der_encode().unwrap();

assert_eq!(
encoded,
Expand All @@ -129,7 +238,7 @@ mod tests {
0b00001001, // L: 0b0 <- short form, 0b0001001 (9) <- length
0b00000010, // T: 0b00 <- universal, 0b0 <- primitive, 0b00010 (2) <- INTEGER tag
0b00000001, // L: 0b0 <- short form, 0b0000001 (1) <- length
0b00000001, // V: 0b00000001 (1) <- value
0b00000000, // V: 0b00000000 (0) <- value
0b10100000, // T: 0b10 <- context-specific, 0b1 <- constructed, 0b00000 (0) <- CHOICE index
0b00000100, // L: 0b0 <- short form, 0b0000100 (4) <- length
0b00000010, // T: 0b00 <- universal, 0b0 <- primitive, 0b00010 (2) <- INTEGER tag
Expand All @@ -140,12 +249,24 @@ mod tests {
);
}

#[test]
fn test_extract_deposit_amount_without_taking_ownership() {
const AMOUNT: u64 = 1000;
let encoded = Deposit::new(AMOUNT);
let encoded_ref = &encoded;

let extracted_amount: u64 = (encoded_ref.amount.clone()).try_into().unwrap();
assert_eq!(extracted_amount, AMOUNT);
}

#[test]
fn test_encode_transfer() {
const NONCE: u64 = 1;
const RECIPIENT: [u8; 32] = [11; 32];
const AMOUNT: u64 = 1000;
let encoded = make_transfer(NONCE, &RECIPIENT, AMOUNT).unwrap();
let encoded = SigningPayload::new_transfer(NONCE, &RECIPIENT, AMOUNT)
.der_encode()
.unwrap();

assert_eq!(
encoded,
Expand All @@ -166,8 +287,35 @@ mod tests {
fn test_encode_withdrawal() {
const NONCE: u64 = 1;
const AMOUNT: u64 = 1000;
let encoded = make_withdrawal(NONCE, AMOUNT).unwrap();
let encoded = SigningPayload::new_withdrawal(NONCE, AMOUNT)
.der_encode()
.unwrap();

assert_eq!(encoded, vec![48, 9, 2, 1, 1, 162, 4, 2, 2, 3, 232]);
}

#[test]
fn test_hex_encode_nixos_end_to_end_payloads() {
fn hex_encode(payload: SigningPayload) -> String {
hex::encode(payload.der_encode().unwrap())
}

let deposit_payload = hex_encode(SigningPayload::new_deposit(1000));
assert_eq!(deposit_payload.as_str(), "3009020100a004020203e8");

let decoded_deadbabe = hex::decode("deadbabe").unwrap();

let transfer_payload = hex_encode(SigningPayload::new_transfer(
0,
decoded_deadbabe.as_slice(),
1000,
));
assert_eq!(
transfer_payload.as_str(),
"300f020100a10a0404deadbabe020203e8"
);

let withdrawal_payload = hex_encode(SigningPayload::new_withdrawal(0, 1000));
assert_eq!(withdrawal_payload.as_str(), "3009020100a204020203e8");
}
}
Loading

0 comments on commit 2a1996c

Please sign in to comment.