Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replaces custom pack mod with starknet StorePacking #373

Merged
merged 3 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,6 @@ jobs:
run: |
cd contracts/market && scarb build && scarb cairo-test

build-pack:
name: Build Pack
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Scarb
run: |
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | bash -s -- -v ${{ env.SCARB_VERSION }}

- name: Scarb build
run: |
cd contracts/pack && scarb build && scarb cairo-test

build-obstacles:
name: Build Obstacles
runs-on: ubuntu-latest
Expand Down
1 change: 0 additions & 1 deletion contracts/adventurer/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ version = "0.1.0"
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest

[dependencies]
pack = { path = "../pack" }
lootitems = { path = "../loot" }
obstacles = { path = "../obstacles" }
combat = { path = "../combat" }
Expand Down
223 changes: 96 additions & 127 deletions contracts/adventurer/src/adventurer.cairo

Large diffs are not rendered by default.

62 changes: 31 additions & 31 deletions contracts/adventurer/src/adventurer_meta.cairo
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
use starknet::{StorePacking};
use traits::{TryInto, Into};
use pack::constants::pow;
use pack::pack::{Packing, rshift_split};

#[derive(Drop, Copy, Serde)]
struct AdventurerMetadata {
name: u128,
entropy: u128,
}

impl PackingAdventurerMetadata of Packing<AdventurerMetadata> {
fn pack(self: AdventurerMetadata) -> felt252 {
(self.entropy.into()
+ self.name.into() * pow::TWO_POW_128)
.try_into()
.expect('pack AdventurerMetadata')
}
fn unpack(packed: felt252) -> AdventurerMetadata {
let packed = packed.into();
let (packed, entropy) = rshift_split(packed, pow::TWO_POW_128);
let (_, name) = rshift_split(packed, pow::TWO_POW_128);
AdventurerMetadata {
name: name.try_into().expect('unpack AdvMetadata name'),
entropy: entropy.try_into().expect('unpack AdvMetadata entropy')
}
}
const TWO_POW_128: u256 = 0x100000000000000000000000000000000;

// TODO: add overflow pack protection
fn overflow_pack_protection(self: AdventurerMetadata) -> AdventurerMetadata {
self
impl PackingAdventurerMetadata of StorePacking<AdventurerMetadata, felt252> {
fn pack(value: AdventurerMetadata) -> felt252 {
(value.entropy.into() + value.name.into() * TWO_POW_128).try_into().unwrap()
}
fn unpack(value: felt252) -> AdventurerMetadata {
let packed = value.into();
let (packed, entropy) = integer::U256DivRem::div_rem(
packed, TWO_POW_128.try_into().unwrap()
);
let (_, name) = integer::U256DivRem::div_rem(packed, TWO_POW_128.try_into().unwrap());
AdventurerMetadata { name: name.try_into().unwrap(), entropy: entropy.try_into().unwrap() }
}
}

#[cfg(test)]
#[test]
#[available_gas(96380)]
#[available_gas(116600)]
fn test_pack_unpack_adventurer_meta() {
let meta = AdventurerMetadata {
name: 'abcdefghijklmno',
entropy: 340282366920938463463374607431768211455
};
// max value case
let max_u128 = 340282366920938463463374607431768211455;
let name_length = 'abcdefghijklmno';

let packed = meta.pack();
let unpacked: AdventurerMetadata = Packing::unpack(packed);
assert(meta.name == unpacked.name, 'name');
assert(meta.entropy == unpacked.entropy, 'entropy');
}
let meta = AdventurerMetadata { name: name_length, entropy: max_u128 };

let packed = PackingAdventurerMetadata::pack(meta);
let unpacked: AdventurerMetadata = PackingAdventurerMetadata::unpack(packed);
assert(meta.name == unpacked.name, 'name should be max');
assert(meta.entropy == unpacked.entropy, 'entropy should be max u128');

// zero case
let meta = AdventurerMetadata { name: 0, entropy: 0 };
let packed = PackingAdventurerMetadata::pack(meta);
let unpacked: AdventurerMetadata = PackingAdventurerMetadata::unpack(packed);
assert(unpacked.name == 0, 'name should be 0');
assert(unpacked.entropy == 0, 'entropy should be 0');
}
118 changes: 41 additions & 77 deletions contracts/adventurer/src/adventurer_stats.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use starknet::{StorePacking};
use option::OptionTrait;
use traits::{TryInto, Into};
use pack::{pack::{Packing, rshift_split}, constants::pow};
use survivor::constants::adventurer_constants::MAX_STAT_VALUE;

#[derive(Drop, Copy, Serde)]
Expand All @@ -14,7 +14,7 @@ struct Stats { // 5 bits each
wisdom: u8, // 5 bits
charisma: u8, // 5 bits
// Metaphysical
luck: u8 // dynamically generated (not stored)
luck: u8 // // not stored - dynamically generated
}

#[generate_trait]
Expand All @@ -26,64 +26,51 @@ impl StatUtils of IStat {
}
}

impl StatsPacking of Packing<Stats> {
fn pack(self: Stats) -> felt252 {
let overflow_protected = self.overflow_pack_protection();

(overflow_protected.strength.into()
+ overflow_protected.dexterity.into() * pow::TWO_POW_5
+ overflow_protected.vitality.into() * pow::TWO_POW_10
+ overflow_protected.intelligence.into() * pow::TWO_POW_15
+ overflow_protected.wisdom.into() * pow::TWO_POW_20
+ overflow_protected.charisma.into() * pow::TWO_POW_25)
const TWO_POW_5: u256 = 0x20;
const TWO_POW_10: u256 = 0x400;
const TWO_POW_15: u256 = 0x8000;
const TWO_POW_20: u256 = 0x100000;
const TWO_POW_25: u256 = 0x2000000;

impl StatsPacking of StorePacking<Stats, felt252> {
fn pack(value: Stats) -> felt252 {
(value.strength.into()
+ value.dexterity.into() * TWO_POW_5
+ value.vitality.into() * TWO_POW_10
+ value.intelligence.into() * TWO_POW_15
+ value.wisdom.into() * TWO_POW_20
+ value.charisma.into() * TWO_POW_25)
.try_into()
.expect('pack Stats')
.unwrap()
}

fn unpack(packed: felt252) -> Stats {
let packed = packed.into();
let (packed, strength) = rshift_split(packed, pow::TWO_POW_5);
let (packed, dexterity) = rshift_split(packed, pow::TWO_POW_5);
let (packed, vitality) = rshift_split(packed, pow::TWO_POW_5);
let (packed, intelligence) = rshift_split(packed, pow::TWO_POW_5);
let (packed, wisdom) = rshift_split(packed, pow::TWO_POW_5);
let (_, charisma) = rshift_split(packed, pow::TWO_POW_5);
fn unpack(value: felt252) -> Stats {
let packed = value.into();
let (packed, strength) = integer::U256DivRem::div_rem(
packed, TWO_POW_5.try_into().unwrap()
);
let (packed, dexterity) = integer::U256DivRem::div_rem(
packed, TWO_POW_5.try_into().unwrap()
);
let (packed, vitality) = integer::U256DivRem::div_rem(
packed, TWO_POW_5.try_into().unwrap()
);
let (packed, intelligence) = integer::U256DivRem::div_rem(
packed, TWO_POW_5.try_into().unwrap()
);
let (packed, wisdom) = integer::U256DivRem::div_rem(packed, TWO_POW_5.try_into().unwrap());
let (_, charisma) = integer::U256DivRem::div_rem(packed, TWO_POW_5.try_into().unwrap());

Stats {
strength: strength.try_into().expect('unpack Stats strength'),
dexterity: dexterity.try_into().expect('unpack Stats dexterity'),
vitality: vitality.try_into().expect('unpack Stats vitality'),
intelligence: intelligence.try_into().expect('unpack Stats intelligence'),
wisdom: wisdom.try_into().expect('unpack Stats wisdom'),
charisma: charisma.try_into().expect('unpack Stats charisma'),
strength: strength.try_into().unwrap(),
dexterity: dexterity.try_into().unwrap(),
vitality: vitality.try_into().unwrap(),
intelligence: intelligence.try_into().unwrap(),
wisdom: wisdom.try_into().unwrap(),
charisma: charisma.try_into().unwrap(),
luck: 0
}
}

fn overflow_pack_protection(self: Stats) -> Stats {
let mut overflow_protected_stats = self;

if self.strength > MAX_STAT_VALUE {
overflow_protected_stats.strength = MAX_STAT_VALUE;
};
if self.dexterity > MAX_STAT_VALUE {
overflow_protected_stats.dexterity = MAX_STAT_VALUE;
}
if self.vitality > MAX_STAT_VALUE {
overflow_protected_stats.vitality = MAX_STAT_VALUE;
}
if self.intelligence > MAX_STAT_VALUE {
overflow_protected_stats.intelligence = MAX_STAT_VALUE;
}
if self.wisdom > MAX_STAT_VALUE {
overflow_protected_stats.wisdom = MAX_STAT_VALUE;
}
if self.charisma > MAX_STAT_VALUE {
overflow_protected_stats.charisma = MAX_STAT_VALUE;
}

overflow_protected_stats
}
}

// ---------------------------
Expand All @@ -103,7 +90,7 @@ mod tests {
strength: 0, dexterity: 0, vitality: 0, intelligence: 0, wisdom: 0, charisma: 0, luck: 0
};

let packed = stats.pack();
let packed = StatsPacking::pack(stats);
let unpacked = StatsPacking::unpack(packed);
assert(stats.strength == unpacked.strength, 'strength zero case');
assert(stats.dexterity == unpacked.dexterity, 'dexterity zero case');
Expand All @@ -124,7 +111,7 @@ mod tests {
luck: 31
};

let packed = stats.pack();
let packed = StatsPacking::pack(stats);
let unpacked = StatsPacking::unpack(packed);
assert(stats.strength == unpacked.strength, 'strength storage limit');
assert(stats.dexterity == unpacked.dexterity, 'dexterity storage limit');
Expand All @@ -133,28 +120,5 @@ mod tests {
assert(stats.wisdom == unpacked.wisdom, 'wisdom storage limit');
assert(stats.charisma == unpacked.charisma, 'charisma storage limit');
assert(unpacked.luck == 0, 'luck is zero from storage');

// overflow storage limit using max u8
let stats = Stats {
strength: 255,
dexterity: 255,
vitality: 255,
intelligence: 255,
wisdom: 255,
charisma: 255,
luck: 255
};

let packed = stats.pack();
let unpacked = StatsPacking::unpack(packed);

// assert packing function prevented overflow
assert(unpacked.strength == MAX_STAT_VALUE, 'strength pack overflow');
assert(unpacked.dexterity == MAX_STAT_VALUE, 'dexterity pack overflow');
assert(unpacked.vitality == MAX_STAT_VALUE, 'vitality pack overflow');
assert(unpacked.intelligence == MAX_STAT_VALUE, 'intelligence pack overflow');
assert(unpacked.wisdom == MAX_STAT_VALUE, 'wisdom pack overflow');
assert(unpacked.charisma == MAX_STAT_VALUE, 'charisma pack overflow');
assert(unpacked.luck == 0, 'luck is zero from storage');
}
}
68 changes: 44 additions & 24 deletions contracts/adventurer/src/adventurer_utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use core::{result::ResultTrait, traits::{TryInto, Into}};
use poseidon::poseidon_hash_span;
use option::OptionTrait;
use array::ArrayTrait;
use integer::{u8_overflowing_add, u16_overflowing_add, u16_overflowing_sub, U128IntoU256};
use integer::{
u8_overflowing_add, u16_overflowing_add, u16_overflowing_sub, U128IntoU256, u256_try_as_non_zero
};
use super::{
constants::{
adventurer_constants::{
Expand All @@ -23,10 +25,6 @@ use lootitems::constants::{
}
};
use combat::constants::CombatEnums::{Type, Tier, Slot};
use pack::{
pack::{rshift_split},
constants::{MASK_16, pow, MASK_8, MASK_3, MASK_BOOL, mask, U128_MASK_8, u128_pow}
};

#[generate_trait]
impl AdventurerUtils of IAdventurerUtils {
Expand Down Expand Up @@ -94,7 +92,9 @@ impl AdventurerUtils of IAdventurerUtils {
hash_span.append(block_number.into());
hash_span.append(block_timestamp.into());
let poseidon: felt252 = poseidon_hash_span(hash_span.span()).into();
let (d, r) = rshift_split(poseidon.into(), U128_MAX.into());
let (d, r) = integer::U256DivRem::div_rem(
poseidon.into(), u256_try_as_non_zero(U128_MAX.into()).unwrap()
);
r.try_into().unwrap()
}

Expand All @@ -107,7 +107,9 @@ impl AdventurerUtils of IAdventurerUtils {
hash_span.append(entropy.into());
hash_span.append(item_id.into());
let poseidon: felt252 = poseidon_hash_span(hash_span.span()).into();
let (d, r) = rshift_split(poseidon.into(), U128_MAX.into());
let (d, r) = integer::U256DivRem::div_rem(
poseidon.into(), u256_try_as_non_zero(U128_MAX.into()).unwrap()
);
(r.try_into().unwrap(), d.try_into().unwrap())
}

Expand Down Expand Up @@ -219,7 +221,9 @@ impl AdventurerUtils of IAdventurerUtils {
// @param felt_to_split: felt to split
// @return (u128, u128): tuple of u128s
fn split_hash(felt_to_split: felt252) -> (u128, u128) {
let (d, r) = rshift_split(felt_to_split.into(), U128_MAX.into());
let (d, r) = integer::U256DivRem::div_rem(
felt_to_split.into(), u256_try_as_non_zero(U128_MAX.into()).unwrap()
);
(r.try_into().unwrap(), d.try_into().unwrap())
}

Expand Down Expand Up @@ -270,25 +274,41 @@ impl AdventurerUtils of IAdventurerUtils {

fn u128_to_u8_array(value: u128) -> Array<u8> {
let mut result = ArrayTrait::<u8>::new();
result.append((value & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_8) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_16) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_24) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_32) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_40) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_48) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_56) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_64) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_72) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_80) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_88) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_96) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_104) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_112) & U128_MASK_8).try_into().unwrap());
result.append(((value / u128_pow::_120) & U128_MASK_8).try_into().unwrap());
result.append((value & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_8) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_16) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_24) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_32) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_40) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_48) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_56) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_64) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_72) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_80) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_88) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_96) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_104) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_112) & MASK_8).try_into().unwrap());
result.append(((value / TWO_POW_120) & MASK_8).try_into().unwrap());
result
}
}
const MASK_8: u128 = 0xFF;
const TWO_POW_8: u128 = 0x100;
const TWO_POW_16: u128 = 0x10000;
const TWO_POW_24: u128 = 0x1000000;
const TWO_POW_32: u128 = 0x100000000;
const TWO_POW_40: u128 = 0x10000000000;
const TWO_POW_48: u128 = 0x1000000000000;
const TWO_POW_56: u128 = 0x100000000000000;
const TWO_POW_64: u128 = 0x10000000000000000;
const TWO_POW_72: u128 = 0x1000000000000000000;
const TWO_POW_80: u128 = 0x100000000000000000000;
const TWO_POW_88: u128 = 0x10000000000000000000000;
const TWO_POW_96: u128 = 0x1000000000000000000000000;
const TWO_POW_104: u128 = 0x100000000000000000000000000;
const TWO_POW_112: u128 = 0x10000000000000000000000000000;
const TWO_POW_120: u128 = 0x1000000000000000000000000000000;

// ---------------------------
// ---------- Tests ----------
Expand Down
Loading