Skip to content

Commit

Permalink
Swap and Ics 20 Withdrawal effect hash (#9)
Browse files Browse the repository at this point in the history
* Add ics20 parser

* add swap parser

* add swap effect hash

* update snapshots
  • Loading branch information
abenso authored Dec 2, 2024
1 parent 4471075 commit 29b468e
Show file tree
Hide file tree
Showing 67 changed files with 1,290 additions and 199 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ file(GLOB_RECURSE LIB_SRC
${CMAKE_CURRENT_SOURCE_DIR}/app/src/plan/output_plan.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/plan/delegate_plan.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/plan/undelegate_plan.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/plan/ics20_withdrawal.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/plan/swap.c
)

add_library(app_lib STATIC ${LIB_SRC})
Expand Down
1 change: 1 addition & 0 deletions app/rust/include/rslib.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ parser_error_t rs_parameter_hash(bytes_t *data, uint8_t *output, size_t output_l
parser_error_t rs_spend_action_hash(spend_key_bytes_t *sk, spend_plan_t *plan, uint8_t *output, size_t output_len);
parser_error_t rs_output_action_hash(spend_key_bytes_t *sk, output_plan_t *plan, bytes_t *memo_key, uint8_t *output,
size_t output_len);
parser_error_t rs_swap_action_hash(spend_key_bytes_t *sk, swap_plan_t *plan, uint8_t *output, size_t output_len);
parser_error_t rs_generic_action_hash(bytes_t *data, uint8_t action_type, uint8_t *output, size_t output_len);

#ifdef __cplusplus
Expand Down
6 changes: 5 additions & 1 deletion app/rust/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub mod address_view;

use crate::constants::ADDRESS_LEN;

#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Debug)]
// pub struct Address([u8; Address::LEN]);
/// A valid payment address.
pub struct Address {
Expand Down Expand Up @@ -106,6 +106,10 @@ impl Address {
&self.pk_d
}

pub fn transmission_key_s(&self) -> &Fq {
&self.tsk
}

pub fn clue_key(&self) -> &ClueKey {
&self.ck_d
}
Expand Down
5 changes: 5 additions & 0 deletions app/rust/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ pub const MEMO_CIPHERTEXT_LEN_BYTES: usize = 528;
pub const MEMO_LEN_BYTES: usize = 512;
// This is the largest text length we can support
pub const MAX_TEXT_LEN: usize = MEMO_LEN_BYTES - ADDRESS_LEN;

// Swap ciphertext byte length.
pub const SWAP_CIPHERTEXT_BYTES: usize = 272;
// Swap plaintext byte length.
pub const SWAP_LEN_BYTES: usize = 256;
2 changes: 1 addition & 1 deletion app/rust/src/keys/dk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use decaf377::{Element, Fq};

use super::spend_key::SpendKeyBytes;

#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Diversifier(pub(crate) [u8; Diversifier::LEN]);

impl Diversifier {
Expand Down
6 changes: 6 additions & 0 deletions app/rust/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ pub mod rseed;
pub mod symmetric;
pub mod value;
pub mod effect_hash;
pub mod swap_plaintext;
pub mod trading_pair;
pub mod fee;
pub mod swap_payload;
pub mod swap_ciphertext;

pub use error::ParserError;

#[derive(Clone, Copy, PartialEq, Eq)]
Expand Down
34 changes: 32 additions & 2 deletions app/rust/src/parser/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* limitations under the License.
********************************************************************************/

use crate::constants::AMOUNT_LEN_BYTES;
use crate::ParserError;
use decaf377::{Fq, Fr};
use crate::constants::AMOUNT_LEN_BYTES;
use crate::utils::protobuf::encode_varint;

#[derive(Clone, Debug)]
pub struct Amount {
Expand All @@ -25,10 +26,39 @@ pub struct Amount {

impl Amount {
pub const LEN: usize = AMOUNT_LEN_BYTES;
pub const PROTO_PREFIX_LO: [u8; 1] = [0x08]; // (1 << 3) | 0 = 8
pub const PROTO_PREFIX_HI: [u8; 1] = [0x10]; // (2 << 3) | 0 = 16

pub fn to_le_bytes(&self) -> [u8; Self::LEN] {
self.inner.to_le_bytes()
}

pub fn to_proto(&self) -> ([u8; 22], usize) {
let mut encoded = [0u8; 22];
let mut pos = 1;

// Get low and high u64s from u128
let lo = self.inner as u64;
let hi = (self.inner >> 64) as u64;

// Only encode non-zero values
if lo != 0 {
encoded[pos] = Self::PROTO_PREFIX_LO[0];
pos += 1;
pos += encode_varint(lo, &mut encoded[pos..]);
}

if hi != 0 {
encoded[pos] = Self::PROTO_PREFIX_HI[0];
pos += 1;
pos += encode_varint(hi, &mut encoded[pos..]);
}

// Add the value of pos to the first byte of encoded
encoded[0] = (pos - 1) as u8;

(encoded, pos)
}
}

impl TryFrom<AmountC> for Amount {
Expand All @@ -53,7 +83,7 @@ impl TryFrom<AmountC> for Amount {
let inner = shifted + lo;

Ok(Amount { inner })
}
}
}

impl Into<Fq> for Amount {
Expand Down
19 changes: 19 additions & 0 deletions app/rust/src/parser/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,23 @@ impl Commitment {
proto[4..].copy_from_slice(&self.0.vartime_compress().0);
proto
}

pub fn to_proto_swap(&self) -> [u8; Self::PROTO_LEN] {
let mut proto = [0u8; Self::PROTO_LEN];
proto[0..4].copy_from_slice(&[0x22, 0x22, 0x0a, 0x20]);
proto[4..].copy_from_slice(&self.0.vartime_compress().0);
proto
}
}

impl StateCommitment {
pub const LEN: usize = 32;
pub const PROTO_LEN: usize = Self::LEN + 4;

pub fn to_proto_swap(&self) -> [u8; Self::PROTO_LEN] {
let mut proto = [0u8; Self::PROTO_LEN];
proto[0..4].copy_from_slice(&[0x0a, 0x22, 0x0a, 0x20]);
proto[4..].copy_from_slice(&self.0.to_bytes());
proto
}
}
63 changes: 63 additions & 0 deletions app/rust/src/parser/fee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::parser::commitment::Commitment;
use crate::parser::id::Id;
use crate::parser::value::Sign;
use crate::parser::value::{Value, ValueC};
use crate::ParserError;
use decaf377::Fq;
use decaf377::Fr;
use crate::constants::{AMOUNT_LEN_BYTES, ID_LEN_BYTES};
// The staking token asset ID (upenumbra)
// Bech32m: passet1984fctenw8m2fpl8a9wzguzp7j34d7vravryuhft808nyt9fdggqxmanqm
pub const STAKING_TOKEN_ASSET_ID_BYTES: [u8; 32] = [
0x29, 0xea, 0x9c, 0x2f, 0x33, 0x71, 0xf6, 0xa4, 0x87, 0xe7, 0xe9, 0x5c, 0x24, 0x70, 0x41, 0xf4,
0xa3, 0x56, 0xf9, 0x83, 0xeb, 0x06, 0x4e, 0x5d, 0x2b, 0x3b, 0xcf, 0x32, 0x2c, 0xa9, 0x6a, 0x10,
];

#[derive(Clone, Debug)]
pub struct Fee(pub Value);

#[repr(C)]
#[derive(Clone)]
#[cfg_attr(any(feature = "derive-debug", test), derive(Debug))]
pub struct FeeC(pub ValueC);

impl TryFrom<FeeC> for Fee {
type Error = ParserError;

fn try_from(value: FeeC) -> Result<Self, Self::Error> {
if value.0.has_asset_id {
Ok(Fee(Value::try_from(value.0)?))
} else {
// If conversion fails, create a new Value with the amount and staking token asset ID
Ok(Fee(Value {
amount: value.0.amount.try_into()?,
asset_id: Id {
0: Fq::from_le_bytes_mod_order(&STAKING_TOKEN_ASSET_ID_BYTES),
},
}))
}
}
}

impl FeeC {
pub fn to_value_c(&self) -> ValueC {
self.0.clone()
}
}

impl Fee {
pub const LEN: usize = AMOUNT_LEN_BYTES + ID_LEN_BYTES;

pub fn commit(&self, blinding: Fr) -> Result<Commitment, ParserError> {
let value = Value::try_from(self.0.clone());
if let Ok(value) = value {
Ok(value.commit(blinding, Sign::Required)?)
} else {
Err(ParserError::ClueCreationFailed)
}
}

pub fn to_bytes(&self) -> Result<[u8; Self::LEN], ParserError> {
self.0.to_bytes()
}
}
21 changes: 2 additions & 19 deletions app/rust/src/parser/memo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::constants::{MEMO_CIPHERTEXT_LEN_BYTES, MEMO_LEN_BYTES};
use crate::ParserError;
use crate::parser::effect_hash::{create_personalized_state, EffectHash};
use crate::parser::bytes::BytesC;
use crate::utils::protobuf::encode_varint;

#[repr(C)]
#[derive(Default)]
Expand Down Expand Up @@ -90,28 +91,10 @@ impl MemoCiphertext {
// Max size needed for u64 varint + 1 byte tag
let mut tag_and_len = [0u8; 11];
tag_and_len[0] = 0x0A; // Tag
let varint_len = Self::encode_varint(len as u64, &mut tag_and_len[1..]);
let varint_len = encode_varint(len as u64, &mut tag_and_len[1..]);

state.update(&tag_and_len[..varint_len + 1]);
state.update(&self.0);
state
}

// TODO: move this to a common place
fn encode_varint(mut value: u64, output: &mut [u8]) -> usize {
let mut i = 0;
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
output[i] = byte;
i += 1;
if value == 0 {
break;
}
}
i
}
}
10 changes: 7 additions & 3 deletions app/rust/src/parser/note_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@ pub struct NotePayload {
impl NotePayload {
pub const LEN: usize = 32 + 32 + NOTE_CIPHERTEXT_BYTES;
pub const PROTO_LEN: usize = Self::LEN + 15;
pub const PROTO_PREFIX_NOTE_COMMITMENT: [u8; 7] = [0x0a, 0xfc, 0x01, 0x0a, 0x22, 0x0a, 0x20];
pub const PROTO_PREFIX_EPHEMERAL_KEY: [u8; 2] = [0x12, 0x20];
pub const PROTO_PREFIX_ENCRYPTED_NOTE: [u8; 6] = [0x1a, 0xb3, 0x01, 0x0a, 0xb0, 0x01];


pub fn to_proto(&self) -> [u8; Self::PROTO_LEN] {
let mut proto = [0u8; Self::PROTO_LEN];
proto[0..7].copy_from_slice(&[0x0a, 0xfc, 0x01, 0x0a, 0x22, 0x0a, 0x20]);
proto[0..7].copy_from_slice(&Self::PROTO_PREFIX_NOTE_COMMITMENT);
proto[7..39].copy_from_slice(&self.note_commitment.0.to_bytes());
proto[39..41].copy_from_slice(&[0x12, 0x20]);
proto[39..41].copy_from_slice(&Self::PROTO_PREFIX_EPHEMERAL_KEY);
proto[41..73].copy_from_slice(&self.ephemeral_key.0);
proto[73..79].copy_from_slice(&[0x1a, 0xb3, 0x01, 0x0a, 0xb0, 0x01]);
proto[73..79].copy_from_slice(&Self::PROTO_PREFIX_ENCRYPTED_NOTE);
proto[79..].copy_from_slice(&self.encrypted_note.0);

proto
Expand Down
3 changes: 2 additions & 1 deletion app/rust/src/parser/nullifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct Nullifier(pub Fq);
impl Nullifier {
pub const LEN: usize = 32;
pub const PROTO_LEN: usize = Self::LEN + 4;
pub const PROTO_PREFIX: [u8; 4] = [0x32, 0x22, 0x0a, 0x20];

/// Derive the [`Nullifier`] for a positioned note or swap given its [`merkle::Position`]
/// and [`Commitment`].
Expand All @@ -42,7 +43,7 @@ impl Nullifier {

pub fn to_proto(&self) -> [u8; Self::PROTO_LEN] {
let mut proto = [0u8; Self::PROTO_LEN];
proto[0..4].copy_from_slice(&[0x32, 0x22, 0x0a, 0x20]);
proto[0..4].copy_from_slice(&Self::PROTO_PREFIX);
proto[4..].copy_from_slice(&self.0.to_bytes());
proto
}
Expand Down
Loading

0 comments on commit 29b468e

Please sign in to comment.