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

Homework 1 #4

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
15 changes: 10 additions & 5 deletions contracts/01-tamagotchi/io/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
#![no_std]

use codec::{Decode, Encode};
use gmeta::Metadata;
use gmeta::{Metadata,In,InOut,Out};
use gstd::prelude::*;
use scale_info::TypeInfo;

#[derive(Default, Encode, Decode, TypeInfo)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub struct Tamagotchi {
// TODO: 1️⃣ Add `name` and `age` fields
pub name: String,
pub date_of_birth: u64,
}

#[derive(Encode, Decode, TypeInfo)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub enum TmgAction {
// TODO: 2️⃣ Add `Name` and `Age` actions that set the name and age
Name,
Age,
}

#[derive(Encode, Decode, TypeInfo)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub enum TmgEvent {
// TODO: 3️⃣ Add `Name` and `Age` events that return the name and age
Name(String),
Age(u64),
}

pub struct ProgramMetadata;

// TODO: 4️⃣ Fill `Init`, `Handle`, and `State` types
impl Metadata for ProgramMetadata {
type Init = ();
type Handle = ();
type State = ();
type Init = In<String>;
type Handle = InOut<TmgAction, TmgEvent>;
type State = Out<Tamagotchi>;
type Reply = ();
type Others = ();
type Signal = ();
Expand Down
29 changes: 27 additions & 2 deletions contracts/01-tamagotchi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
#![no_std]

#[allow(unused_imports)]
use gstd::prelude::*;
use gstd::{exec, msg, prelude::*};
use tamagotchi_io::*;

static mut TAMAGOTCHI: Option<Tamagotchi> = None;

#[no_mangle]
extern fn init() {
// TODO: 5️⃣ Initialize the Tamagotchi program
let initname = msg::load().expect("unable to load name");
let birthdate = exec::block_timestamp();
let tmg = Tamagotchi {
name: initname,
date_of_birth: birthdate,
};
unsafe {
TAMAGOTCHI = Some(tmg);
};
}

#[no_mangle]
extern fn handle() {
// TODO: 6️⃣ Add handling of `Name` and `Age` actions
let action: TmgAction = msg::load().expect("unable to load action");
let tmg = unsafe { TAMAGOTCHI.get_or_insert(Default::default()) };
match action {
TmgAction::Name => {
msg::reply(TmgEvent::Name(tmg.name.clone()), 0)
.expect("Error in a reply'tamagotchi::name'");
}
TmgAction::Age => {
let age = exec::block_timestamp() - tmg.date_of_birth;
msg::reply(TmgEvent::Age(age), 0).expect("Error in a reply'tamagotchi::age'");
}
}
}

#[no_mangle]
extern fn state() {
// TODO: 7️⃣ Return the Tamagotchi state
let tmg = unsafe { TAMAGOTCHI.take().expect("Unexpected error in taking state") };
msg::reply(tmg, 0).expect("Failed to share state");
}
35 changes: 32 additions & 3 deletions contracts/01-tamagotchi/tests/smoke.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
use gtest::{Program, System};
use gtest::{Log, Program, System};
use tamagotchi_io::*;

#[test]
fn smoke_test() {
let sys = System::new();
sys.init_logger();
let _program = Program::current(&sys);
let program = Program::current(&sys);
let result = program.send(2, String::from("Goodmoring"));
assert!(!result.main_failed());
let result = program.send(2, TmgAction::Name);
let log = Log::builder()
.dest(2)
.payload(TmgEvent::Name(String::from("Goodmoring")));
assert!(result.contains(&log));
let _result = program.send(2, TmgAction::Age);
// let log = Log::builder().dest(2).payload(TmgEvent::Age(sys.block_timestamp()));
// assert!(result.contains(&log));

// TODO: 8️⃣ Test the program initialization and message handling
//How to test the age?
}

#[test]
fn negative_smoke_test() {
let sys = System::new();
sys.init_logger();
let program = Program::current(&sys);
let payload = vec![1, 2, 3];
let _result = program.send(2, payload);
// assert!(result.main_failed());
// Why the assert is panic?

// let result = program.send(1, TmgAction::Name);
// let log = Log::builder().dest(2).payload(TmgEvent::Name("Goodmoring".to_string()));
// assert!(!result.contains(&log));
// let result = program.send(1, TmgAction::Age);
// let log = Log::builder().dest(2).payload(TmgEvent::Age(sys.block_timestamp()));
// assert!(!result.contains(&log));
}
25 changes: 23 additions & 2 deletions contracts/02-tamagotchi-interaction/io/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![no_std]

use codec::{Decode, Encode};
use gmeta::{Metadata,In,InOut,Out};
use gstd::prelude::*;
use gmeta::{In, InOut, Metadata, Out};
use gstd::{prelude::*, ActorId};

#[derive(Default, Encode, Decode, TypeInfo)]
#[codec(crate = gstd::codec)]
Expand All @@ -12,6 +12,13 @@ pub struct Tamagotchi {
pub name: String,
pub date_of_birth: u64,
// TODO: 1️⃣ Add new fields
pub owner: ActorId,
pub fed: u64,
pub fed_block: u64,
pub entertained: u64,
pub entertained_block: u64,
pub slept: u64,
pub slept_block: u64,
}

#[derive(Encode, Decode, TypeInfo)]
Expand All @@ -22,6 +29,9 @@ pub enum TmgAction {
Name,
Age,
// TODO: 2️⃣ Add new actions
Feed,
Entertain,
Sleep,
}

#[derive(Encode, Decode, TypeInfo)]
Expand All @@ -32,6 +42,9 @@ pub enum TmgEvent {
Name(String),
Age(u64),
// TODO: 3️⃣ Add new events
Fed,
Entertained,
Slept,
}

pub struct ProgramMetadata;
Expand All @@ -45,3 +58,11 @@ impl Metadata for ProgramMetadata {
type Others = ();
type Signal = ();
}

pub const HUNGER_PER_BLOCK: u64 = 1;
pub const BOREDOM_PER_BLOCK: u64 = 2;
pub const ENERGY_PER_BLOCK: u64 = 2;

pub const FILL_PER_FEED: u64 = 1000;
pub const FILL_PER_ENTERTAINMENT: u64 = 1000;
pub const FILL_PER_SLEEP: u64 = 1000;
124 changes: 113 additions & 11 deletions contracts/02-tamagotchi-interaction/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,64 @@
#![no_std]

#[allow(unused_imports)]
use gstd::prelude::*;
use gstd::{exec, msg, prelude::*, ActorId};
use tamagotchi_interaction_io::*;

#[derive(Default, Encode, Decode, TypeInfo)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub struct Tamagotchi {
pub name: String,
pub date_of_birth: u64,
pub owner: ActorId,
pub fed: u64,
pub fed_block: u64,
pub entertained: u64,
pub entertained_block: u64,
pub slept: u64,
pub slept_block: u64,
}

impl Tamagotchi {
fn current_fed(&mut self) -> u64 {
let a: u64 =
self.fed - (HUNGER_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.fed_block);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If self.fed is less than HUNGER_PER_BLOCK * (exec::block_height() - self.fed_block), then subtraction results in the underflow.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

a

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create a var:

Suggested change
let a: u64 =
self.fed - (HUNGER_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.fed_block);
a
self.fed - HUNGER_PER_BLOCK * ((exec::block_height() as u64) - self.fed_block)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

}
fn current_entertained(&mut self) -> u64 {
let b: u64 = self.entertained
- (BOREDOM_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.entertained_block);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using self.entertained_block is not correct here. We need to save the last processed block height here.

Suggested change
- (BOREDOM_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.entertained_block);
- (BOREDOM_PER_BLOCK as u64) * ((exec::block_height() as u64) - last_processed_block);

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have fixed it.

b
}
fn current_slept(&mut self) -> u64 {
let c: u64 = self.slept
- (ENERGY_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.slept_block);
c
}
}

static mut TAMAGOTCHI: Option<Tamagotchi> = None;

// TODO: 4️⃣ Define constants

#[no_mangle]
extern fn init() {
// TODO: 0️⃣ Copy the `init` function from the previous lesson and push changes to the master branch
let initname = msg::load().expect("unable to load name");
let birthdate = exec::block_timestamp();
let birthdate = exec::block_height() as u64;
let fedblock = exec::block_height() as u64;
let entertainedblock = exec::block_height() as u64;
let sleptblock = exec::block_height() as u64;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to call exec::block_height multiple times as it is quite expensive. Better is to get block height one time and copy it.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

let tmg = Tamagotchi {
name: initname,
date_of_birth: birthdate,
owner: msg::source(),
fed: 1000,
fed_block: fedblock,
entertained: 5000,
entertained_block: entertainedblock,
slept: 2000,
slept_block: sleptblock,
};
unsafe {
TAMAGOTCHI = Some(tmg);
Expand All @@ -24,17 +70,73 @@ extern fn handle() {
// TODO: 0️⃣ Copy the `handle` function from the previous lesson and push changes to the master branch
let action: TmgAction = msg::load().expect("unable to load action");
let tmg = unsafe { TAMAGOTCHI.get_or_insert(Default::default()) };
match action {
TmgAction::Name => {
msg::reply(TmgEvent::Name(tmg.name.clone()), 0)
.expect("Error in a reply'tamagotchi::name'");
}
TmgAction::Age => {
let age = exec::block_timestamp() - tmg.date_of_birth;
msg::reply(TmgEvent::Age(age), 0).expect("Error in a reply'tamagotchi::age'");
if msg::source() == tmg.owner {
match action {
TmgAction::Name => {
msg::reply(TmgEvent::Name(tmg.name.clone()), 0)
.expect("Error in a reply'tamagotchi::name'");
}
TmgAction::Age => {
let age = exec::block_timestamp() - tmg.date_of_birth;
msg::reply(TmgEvent::Age(age), 0).expect("Error in a reply'tamagotchi::age'");
}
TmgAction::Feed => {
if tmg.current_fed() <= 9000 {
let fed = tmg.fed + FILL_PER_FEED;
msg::reply(TmgEvent::Fed, 0).expect("Error in a reply'tamagotchi::fed'");
tmg.fed = fed;
tmg.fed_block = exec::block_height() as u64;
tmg.entertained = tmg.current_entertained();
tmg.slept = tmg.current_slept();
} else {
let fedblock = exec::block_height() as u64;
tmg.fed = 10000;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to use named constants for numbers.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

tmg.fed_block = fedblock;
tmg.entertained = tmg.current_entertained();
tmg.slept = tmg.current_slept();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same code is in both if branches.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

msg::reply(TmgEvent::Fed, 1).expect("Error in a reply'tamagotchi::fed'");
}
}
TmgAction::Entertain => {
if tmg.current_entertained() <= 9000 {
let entertained = tmg.entertained + FILL_PER_ENTERTAINMENT;
msg::reply(TmgEvent::Entertained, 0)
.expect("Error in a reply'tamagotchi::entertained'");
tmg.entertained = entertained;
tmg.entertained_block = exec::block_height() as u64;
tmg.fed = tmg.current_fed();
tmg.slept = tmg.current_slept();
} else {
let entertainedblock = exec::block_height() as u64;
tmg.entertained = 10000;
tmg.entertained_block = entertainedblock;
tmg.fed = tmg.current_fed();
tmg.slept = tmg.current_slept();
msg::reply(TmgEvent::Entertained, 1)
.expect("Error in a reply'tamagotchi::entertained'");
}
}
TmgAction::Sleep => {
if tmg.current_slept() <= 9000 {
let slept = tmg.slept + FILL_PER_SLEEP;
msg::reply(TmgEvent::Slept, 0).expect("Error in a reply'tamagotchi::slept'");
tmg.slept = slept;
tmg.slept_block = exec::block_height() as u64;
tmg.fed = tmg.current_fed();
tmg.entertained = tmg.current_entertained();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, you can move the fed / entertained / slept decreasing outside this match.

} else {
let sleptblock = exec::block_height() as u64;
tmg.slept = 10000;
tmg.slept_block = sleptblock;
tmg.fed = tmg.current_fed();
tmg.entertained = tmg.current_entertained();
msg::reply(TmgEvent::Slept, 1).expect("Error in a reply'tamagotchi::slept'");
}
}
}
} else {
panic!("You are not the owner of this tamagotchi");
}
// TODO: 5️⃣ Add new logic for calculating the `fed`, `entertained` and `slept` levels
}

#[no_mangle]
Expand Down
21 changes: 18 additions & 3 deletions contracts/02-tamagotchi-interaction/tests/interaction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use gtest::{Program, System};
use gtest::{Log, Program, System};
use tamagotchi_interaction_io::*;

// TODO: 0️⃣ Copy tests from the previous lesson and push changes to the master branch

Expand Down Expand Up @@ -43,6 +44,20 @@ fn negative_smoke_test() {
fn interaction_test() {
let sys = System::new();
sys.init_logger();
let _program = Program::current(&sys);
// TODO: 6️⃣ Test new functionality
let program = Program::current(&sys);
let result = program.send(2, String::from("Goodmoring"));
assert!(!result.main_failed());
let result = program.send(2, TmgAction::Feed);
let log = Log::builder().dest(2).payload(TmgEvent::Fed);
assert!(result.contains(&log));
let result = program.send(2, TmgAction::Entertain);
let log = Log::builder().dest(2).payload(TmgEvent::Entertained);
assert!(result.contains(&log));
let result = program.send(2, TmgAction::Sleep);
let log = Log::builder().dest(2).payload(TmgEvent::Slept);
assert!(result.contains(&log));

let _result = program.send(1, TmgAction::Sleep);
//how to test the panic result?
//negetive test
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to check the Tamagotchi's vital signs after spending various counts of blocks.

Loading