Skip to content

Commit

Permalink
starknet: update to cairo 2.4.3 (byte array and new macro)
Browse files Browse the repository at this point in the history
Update scarb to 2.4.3
Update starknet foundry to 0.13.1
  • Loading branch information
ptisserand committed Feb 6, 2024
1 parent b035684 commit b4b18a6
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 640 deletions.
2 changes: 2 additions & 0 deletions apps/blockchain/starknet/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scarb 2.4.3
starknet-foundry 0.13.1
5 changes: 3 additions & 2 deletions apps/blockchain/starknet/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ name = "starklane"
version = "0.1.0"

[dependencies]
starknet = ">=2.1.0"
#snforge_std = { path = "../../../../../starknet/starknet-foundry/snforge_std/" }
starknet = ">=2.4.0"
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.13.1" }

[[target.starknet-contract]]
sierra = true
casm = true

[lib]

17 changes: 9 additions & 8 deletions apps/blockchain/starknet/src/bridge.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ mod bridge {
use option::OptionTrait;
use debug::PrintTrait;

use core::starknet::SyscallResultTrait;

use starknet::{ClassHash, ContractAddress, EthAddress};
use starknet::contract_address::ContractAddressZeroable;
use starknet::eth_address::EthAddressZeroable;

use starklane::interfaces::{IStarklane, IUpgradeable};
use starklane::string::LongString;
use starklane::request::{
Request,
compute_request_header_v1,
Expand Down Expand Up @@ -94,8 +95,8 @@ mod bridge {
l1_addr: EthAddress,
#[key]
l2_addr: ContractAddress,
name: LongString,
symbol: LongString
name: ByteArray,
symbol: ByteArray
}

/// Process message from L1 to withdraw token.
Expand Down Expand Up @@ -239,7 +240,7 @@ mod bridge {
let erc721_metadata = erc721_metadata(collection_l2, Option::Some(token_ids));
let (name, symbol, base_uri, uris) = match erc721_metadata {
Option::Some(data) => (data.name, data.symbol, data.base_uri, data.uris),
Option::None => (''.into(), ''.into(), ''.into(), array![].span())
Option::None => ("", "", "", array![].span())
};

escrow_deposit_tokens(ref self, collection_l2, from, token_ids);
Expand Down Expand Up @@ -342,8 +343,8 @@ mod bridge {
let l2_addr_from_deploy = deploy_erc721_bridgeable(
self.erc721_bridgeable_class.read(),
salt,
*req.name,
*req.symbol,
req.name.clone(),
req.symbol.clone(),
starknet::get_contract_address(),
);

Expand All @@ -355,8 +356,8 @@ mod bridge {
CollectionDeployedFromL1 {
l1_addr: *req.collection_l1,
l2_addr: l2_addr_from_deploy,
name: *req.name,
symbol: *req.symbol
name: req.name.clone(),
symbol: req.symbol.clone()
}
);

Expand Down
98 changes: 98 additions & 0 deletions apps/blockchain/starknet/src/byte_array_extra.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use core::serde::Serde;

impl FeltTryIntoByteArray of TryInto<felt252, ByteArray> {
fn try_into(self: felt252) -> Option<ByteArray> {
let mut res: ByteArray = Default::default();
res.pending_word = self;
let mut length = 0;
let mut data: u256 = self.into();
loop {
if data == 0 {
break;
}
data /= 0x100;
length += 1;
};
res.pending_word_len = length;
Option::Some(res)
}
}


impl SpanFeltTryIntoByteArray of TryInto<Span<felt252>, ByteArray> {
fn try_into(self: Span<felt252>) -> Option<ByteArray> {
if self.len() == 0_usize {
Option::None(())
} else if self.len() == 1_usize {
(*self[0]).try_into()
} else {
let mut self = self.clone();
Serde::deserialize(ref self)
}
}
}



#[cfg(test)]
mod tests {
use core::serde::Serde;
use super::{FeltTryIntoByteArray, SpanFeltTryIntoByteArray};

#[test]
fn from_felt252() {
let a = 'hello how are you?';
let b: Option<ByteArray> = a.try_into();
match b {
Option::Some(e) => {
assert!(e.data.is_empty(), "Data should be empty");
assert_eq!(e.pending_word, 'hello how are you?', "Wrong pending word");
assert_eq!(e.pending_word_len, 18, "Wrong pending word len");
},
Option::None => panic!("Should not be None")
}
}
#[test]
fn from_span_felt252_none() {
let a: Array<felt252> = array![];
let b: Option<ByteArray> = a.span().try_into();
match b {
Option::Some(_) => panic!("Should be None"),
Option::None => {},
}
}

#[test]
fn from_span_felt252_felt252() {
let a: Array<felt252> = array!['hello'];
let b: Option<ByteArray> = a.span().try_into();
match b {
Option::Some(e) => assert_eq!(e, "hello", "String mismatch"),
Option::None => panic!("Should not be None"),
}
}

#[test]
fn from_span_felt252_bytearray_shortstring() {
let orig: ByteArray = "I'm here";
let mut a = ArrayTrait::new();
orig.serialize(ref a);
let b: Option<ByteArray> = a.span().try_into();
match b {
Option::Some(e) => assert_eq!(e, orig, "String mismatch"),
Option::None(e) => panic!("Should not be None"),
}
}

#[test]
fn bytearray_long_serialize() {
let orig: ByteArray = "This palindrome is not as good, but at least it's long enough";
let mut a = ArrayTrait::new();
orig.serialize(ref a);
assert_eq!(*a[0], 1, "Wrong data len");
assert_eq!(*a[1], 0x546869732070616c696e64726f6d65206973206e6f7420617320676f6f642c, "Wrong data[0]");
assert_eq!(*a[2], 0x20627574206174206c656173742069742773206c6f6e6720656e6f756768, "Wrong pending word");
assert_eq!(*a[3], 30, "Wrong pending word len");
}

}
158 changes: 158 additions & 0 deletions apps/blockchain/starknet/src/byte_array_storage.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
///
/// TMP - remove once ByteArrayStore is merged to corelib
///

use core::bytes_31::BYTES_IN_BYTES31;
use core::serde::Serde;
use starknet::storage_access::{
StorageBaseAddress, storage_address_from_base, storage_address_from_base_and_offset,
StorageAddress, storage_base_address_from_felt252
};
use starknet::storage_access;
use starknet::{
SyscallResult, syscalls::{storage_read_syscall, storage_write_syscall},
contract_address::{ContractAddress, Felt252TryIntoContractAddress, ContractAddressIntoFelt252},
class_hash::{ClassHash, Felt252TryIntoClassHash, ClassHashIntoFelt252}
};

impl ByteArrayStore of starknet::Store<ByteArray> {
#[inline(always)]
fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<ByteArray> {
inner_read_byte_array(address_domain, storage_address_from_base(base))
}
#[inline(always)]
fn write(address_domain: u32, base: StorageBaseAddress, value: ByteArray) -> SyscallResult<()> {
inner_write_byte_array(address_domain, storage_address_from_base(base), value)
}
#[inline(always)]
fn read_at_offset(
address_domain: u32, base: StorageBaseAddress, offset: u8
) -> SyscallResult<ByteArray> {
inner_read_byte_array(address_domain, storage_address_from_base_and_offset(base, offset))
}
#[inline(always)]
fn write_at_offset(
address_domain: u32, base: StorageBaseAddress, offset: u8, value: ByteArray
) -> SyscallResult<()> {
inner_write_byte_array(
address_domain, storage_address_from_base_and_offset(base, offset), value
)
}
#[inline(always)]
fn size() -> u8 {
1
}
}

/// Returns a pointer to the `chunk`'th chunk of the byte array at `address`.
/// The pointer is a `Poseidon` hash over:
/// * `address` - The address "containing" the ByteArray (but actually stores just the length).
/// * `chunk` - The index of the chunk.
/// * The short string `ByteArray` as the capacity.
fn inner_byte_array_pointer(address: StorageAddress, chunk: felt252) -> StorageBaseAddress {
let (r, _, _) = core::poseidon::hades_permutation(address.into(), chunk, 'ByteArray'_felt252);
storage_base_address_from_felt252(r)
}

/// Reads a byte array from storage from domain `address_domain` and address `address`.
/// The length of the byte array is read from `address` at domain `address_domain`.
/// For more info read the documentation of `ByteArrayStore`.
fn inner_read_byte_array(address_domain: u32, address: StorageAddress) -> SyscallResult<ByteArray> {
let len: usize =
match starknet::syscalls::storage_read_syscall(address_domain, address)?.try_into() {
Option::Some(x) => x,
Option::None => { return SyscallResult::Err(array!['Invalid ByteArray length']); },
};
let (mut remaining_full_words, pending_word_len) = core::DivRem::div_rem(
len, BYTES_IN_BYTES31.try_into().unwrap()
);
let mut chunk = 0;
let mut chunk_base = inner_byte_array_pointer(address, chunk);
let mut index_in_chunk = 0_u8;
let mut result: ByteArray = Default::default();
loop {
if remaining_full_words == 0 {
break Result::Ok(());
}
let value =
match starknet::syscalls::storage_read_syscall(
address_domain, storage_address_from_base_and_offset(chunk_base, index_in_chunk)
) {
Result::Ok(value) => value,
Result::Err(err) => { break Result::Err(err); },
};
let value: bytes31 = match value.try_into() {
Option::Some(x) => x,
Option::None => { break Result::Err(array!['Invalid value']); },
};
result.data.append(value);
remaining_full_words -= 1;
index_in_chunk = match core::integer::u8_overflowing_add(index_in_chunk, 1) {
Result::Ok(x) => x,
Result::Err(_) => {
// After reading 256 `bytes31`s `index_in_chunk` will overflow and we move to the
// next chunk.
chunk += 1;
chunk_base = inner_byte_array_pointer(address, chunk);
0
},
};
}?;
if pending_word_len != 0 {
result
.pending_word =
starknet::syscalls::storage_read_syscall(
address_domain, storage_address_from_base_and_offset(chunk_base, index_in_chunk)
)?;
result.pending_word_len = pending_word_len;
}
Result::Ok(result)
}

/// Writes a byte array to storage to domain `address_domain` and address `address`.
/// The length of the byte array is written to `address` at domain `address_domain`.
/// For more info read the documentation of `ByteArrayStore`.
fn inner_write_byte_array(
address_domain: u32, address: StorageAddress, value: ByteArray
) -> SyscallResult<()> {
let len = value.len();
starknet::syscalls::storage_write_syscall(address_domain, address, len.into())?;
let mut full_words = value.data.span();
let mut chunk = 0;
let mut chunk_base = inner_byte_array_pointer(address, chunk);
let mut index_in_chunk = 0_u8;
loop {
let curr_value = match full_words.pop_front() {
Option::Some(x) => x,
Option::None => { break Result::Ok(()); },
};
match starknet::syscalls::storage_write_syscall(
address_domain,
storage_address_from_base_and_offset(chunk_base, index_in_chunk),
(*curr_value).into()
) {
Result::Ok(_) => {},
Result::Err(err) => { break Result::Err(err); },
};
index_in_chunk = match core::integer::u8_overflowing_add(index_in_chunk, 1) {
Result::Ok(x) => x,
Result::Err(_) => {
// After writing 256 `byte31`s `index_in_chunk` will overflow and we move to the
// next chunk.
chunk += 1;
chunk_base = inner_byte_array_pointer(address, chunk);
0
},
};
}?;
if value.pending_word_len != 0 {
starknet::syscalls::storage_write_syscall(
address_domain,
storage_address_from_base_and_offset(chunk_base, index_in_chunk),
value.pending_word
)?;
}
Result::Ok(())
}


4 changes: 3 additions & 1 deletion apps/blockchain/starknet/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod string;
mod request;
mod interfaces;
mod bridge;
mod token;

mod byte_array_storage;
mod byte_array_extra;

mod tests;
Loading

0 comments on commit b4b18a6

Please sign in to comment.