mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-04 06:13:10 +00:00
add init function to the token program
This commit is contained in:
parent
702882c6ee
commit
c7bcd20a38
@ -3,7 +3,7 @@ use nssa_core::{
|
|||||||
program::{ProgramInput, read_nssa_inputs, write_nssa_outputs},
|
program::{ProgramInput, read_nssa_inputs, write_nssa_outputs},
|
||||||
};
|
};
|
||||||
|
|
||||||
// The token program has two functions:
|
// The token program has three functions:
|
||||||
// 1. New token definition.
|
// 1. New token definition.
|
||||||
// Arguments to this function are:
|
// Arguments to this function are:
|
||||||
// * Two **default** accounts: [definition_account, holding_account].
|
// * Two **default** accounts: [definition_account, holding_account].
|
||||||
@ -18,6 +18,11 @@ use nssa_core::{
|
|||||||
// * Two accounts: [sender_account, recipient_account].
|
// * Two accounts: [sender_account, recipient_account].
|
||||||
// * An instruction data byte string of length 23, indicating the total supply with the following layout
|
// * An instruction data byte string of length 23, indicating the total supply with the following layout
|
||||||
// [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
// [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||||
|
// 3. Initialize account with zero balance
|
||||||
|
// Arguments to this function are:
|
||||||
|
// * Two accounts: [definition_account, account_to_initialize].
|
||||||
|
// * An dummy byte string of length 23, with the following layout
|
||||||
|
// [0x02 || 0x00 || 0x00 || 0x00 || ... || 0x00 || 0x00].
|
||||||
|
|
||||||
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
||||||
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
||||||
@ -45,6 +50,25 @@ impl TokenDefinition {
|
|||||||
bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes());
|
bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes());
|
||||||
bytes.into()
|
bytes.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse(data: &[u8]) -> Option<Self> {
|
||||||
|
if data.len() != TOKEN_DEFINITION_DATA_SIZE || data[0] != TOKEN_DEFINITION_TYPE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let account_type = data[0];
|
||||||
|
let name = data[1..7].try_into().unwrap();
|
||||||
|
let total_supply = u128::from_le_bytes(
|
||||||
|
data[7..]
|
||||||
|
.try_into()
|
||||||
|
.expect("Total supply must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
|
Some(Self {
|
||||||
|
account_type,
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenHolding {
|
impl TokenHolding {
|
||||||
@ -61,8 +85,16 @@ impl TokenHolding {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let account_type = data[0];
|
let account_type = data[0];
|
||||||
let definition_id = AccountId::new(data[1..33].try_into().unwrap());
|
let definition_id = AccountId::new(
|
||||||
let balance = u128::from_le_bytes(data[33..].try_into().unwrap());
|
data[1..33]
|
||||||
|
.try_into()
|
||||||
|
.expect("Defintion ID must be 32 bytes long"),
|
||||||
|
);
|
||||||
|
let balance = u128::from_le_bytes(
|
||||||
|
data[33..]
|
||||||
|
.try_into()
|
||||||
|
.expect("balance must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
Some(Self {
|
Some(Self {
|
||||||
definition_id,
|
definition_id,
|
||||||
balance,
|
balance,
|
||||||
@ -167,6 +199,33 @@ fn new_definition(
|
|||||||
vec![definition_target_account_post, holding_target_account_post]
|
vec![definition_target_account_post, holding_target_account_post]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<Account> {
|
||||||
|
if pre_states.len() != 2 {
|
||||||
|
panic!("Invalid number of accounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition = &pre_states[0];
|
||||||
|
let account_to_initialize = &pre_states[1];
|
||||||
|
|
||||||
|
if account_to_initialize.account != Account::default() {
|
||||||
|
panic!("Only uninitialized accounts can be initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We should check that this is an account owned by the token program.
|
||||||
|
// This check can't be done here since the ID of the program is known only after compiling it
|
||||||
|
//
|
||||||
|
// Check definition account is valid
|
||||||
|
let _definition_values =
|
||||||
|
TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid");
|
||||||
|
let holding_values = TokenHolding::new(&definition.account_id);
|
||||||
|
|
||||||
|
let definition_post = definition.account.clone();
|
||||||
|
let mut account_to_initialize_post = account_to_initialize.account.clone();
|
||||||
|
account_to_initialize_post.data = holding_values.into_data();
|
||||||
|
|
||||||
|
vec![definition_post, account_to_initialize_post]
|
||||||
|
}
|
||||||
|
|
||||||
type Instruction = [u8; 23];
|
type Instruction = [u8; 23];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -175,36 +234,59 @@ fn main() {
|
|||||||
instruction,
|
instruction,
|
||||||
} = read_nssa_inputs::<Instruction>();
|
} = read_nssa_inputs::<Instruction>();
|
||||||
|
|
||||||
match instruction[0] {
|
let (pre_states, post_states) = match instruction[0] {
|
||||||
0 => {
|
0 => {
|
||||||
// Parse instruction
|
// Parse instruction
|
||||||
let total_supply = u128::from_le_bytes(instruction[1..17].try_into().unwrap());
|
let total_supply = u128::from_le_bytes(
|
||||||
let name: [u8; 6] = instruction[17..].try_into().unwrap();
|
instruction[1..17]
|
||||||
|
.try_into()
|
||||||
|
.expect("Total supply must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
|
let name: [u8; 6] = instruction[17..]
|
||||||
|
.try_into()
|
||||||
|
.expect("Name must be 6 bytes long");
|
||||||
assert_ne!(name, [0; 6]);
|
assert_ne!(name, [0; 6]);
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
let post_states = new_definition(&pre_states, name, total_supply);
|
let post_states = new_definition(&pre_states, name, total_supply);
|
||||||
write_nssa_outputs(pre_states, post_states);
|
(pre_states, post_states)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
// Parse instruction
|
// Parse instruction
|
||||||
let balance_to_move = u128::from_le_bytes(instruction[1..17].try_into().unwrap());
|
let balance_to_move = u128::from_le_bytes(
|
||||||
let name: [u8; 6] = instruction[17..].try_into().unwrap();
|
instruction[1..17]
|
||||||
|
.try_into()
|
||||||
|
.expect("Balance to move must be 16 bytes little-endian"),
|
||||||
|
);
|
||||||
|
let name: [u8; 6] = instruction[17..]
|
||||||
|
.try_into()
|
||||||
|
.expect("Name must be 6 bytes long");
|
||||||
assert_eq!(name, [0; 6]);
|
assert_eq!(name, [0; 6]);
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
let post_states = transfer(&pre_states, balance_to_move);
|
let post_states = transfer(&pre_states, balance_to_move);
|
||||||
write_nssa_outputs(pre_states, post_states);
|
(pre_states, post_states)
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// Initialize account
|
||||||
|
assert_eq!(instruction[1..], [0; 22]);
|
||||||
|
let post_states = initialize_account(&pre_states);
|
||||||
|
(pre_states, post_states)
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid instruction"),
|
_ => panic!("Invalid instruction"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
write_nssa_outputs(pre_states, post_states);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
||||||
|
|
||||||
use crate::{new_definition, transfer, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE};
|
use crate::{
|
||||||
|
TOKEN_DEFINITION_DATA_SIZE, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE,
|
||||||
|
initialize_account, new_definition, transfer,
|
||||||
|
};
|
||||||
|
|
||||||
#[should_panic(expected = "Invalid number of input accounts")]
|
#[should_panic(expected = "Invalid number of input accounts")]
|
||||||
#[test]
|
#[test]
|
||||||
@ -551,4 +633,37 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_token_initialize_account_succeeds() {
|
||||||
|
let pre_states = vec![
|
||||||
|
AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
// Definition ID with
|
||||||
|
data: vec![0; TOKEN_DEFINITION_DATA_SIZE - 16]
|
||||||
|
.into_iter()
|
||||||
|
.chain(u128::to_le_bytes(1000))
|
||||||
|
.collect(),
|
||||||
|
..Account::default()
|
||||||
|
},
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: AccountId::new([1; 32]),
|
||||||
|
},
|
||||||
|
AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: AccountId::new([2; 32]),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let post_states = initialize_account(&pre_states);
|
||||||
|
let [definition, holding] = post_states.try_into().ok().unwrap();
|
||||||
|
assert_eq!(definition.data, pre_states[0].account.data);
|
||||||
|
assert_eq!(
|
||||||
|
holding.data,
|
||||||
|
vec![
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user