Unverified Commit 626bfc27 authored by Michael Müller's avatar Michael Müller Committed by GitHub
Browse files

Migrate examples to new APIs (#487)

* [examples] Make multisig_plain use Lazy::set()

* [examples] Migrate multisig use HashMap Entry API

* [examples] Migrate DNS to HashMap Entry API

* [examples] Migrate Erc271 to HashMap Entry API

* [examples] Fix naming

* [examples] Increase test coverage

* [examples] Rename fn

* [examples] Address comments
parent c22562ac
Pipeline #107964 passed with stages
in 10 minutes and 22 seconds
......@@ -20,6 +20,7 @@ use ink_lang as ink;
mod dns {
#[cfg(not(feature = "ink-as-dependency"))]
use ink_core::storage2::{
collections::hashmap::Entry,
collections::HashMap as StorageHashMap,
lazy::Lazy,
};
......@@ -107,11 +108,14 @@ mod dns {
#[ink(message)]
fn register(&mut self, name: Hash) -> Result<()> {
let caller = self.env().caller();
if self.is_name_assigned(name) {
return Err(Error::NameAlreadyExists)
let entry = self.name_to_owner.entry(name);
match entry {
Entry::Occupied(_) => return Err(Error::NameAlreadyExists),
Entry::Vacant(vacant) => {
vacant.insert(caller);
self.env().emit_event(Register { name, from: caller });
}
}
self.name_to_owner.insert(name, caller);
self.env().emit_event(Register { name, from: caller });
Ok(())
}
......@@ -157,12 +161,6 @@ mod dns {
self.get_address_or_default(name)
}
/// Returns `true` if the name already assigned.
#[ink(message)]
fn is_name_assigned(&self, name: Hash) -> bool {
self.name_to_owner.get(&name).is_some()
}
/// Returns the owner given the hash or the default address.
fn get_owner_or_default(&self, name: Hash) -> AccountId {
*self
......
......@@ -69,7 +69,10 @@ use ink_lang as ink;
#[ink::contract(version = "0.1.0")]
mod erc721 {
#[cfg(not(feature = "ink-as-dependency"))]
use ink_core::storage2::collections::HashMap as StorageHashMap;
use ink_core::storage2::collections::{
hashmap::Entry,
HashMap as StorageHashMap,
};
use scale::{
Decode,
Encode,
......@@ -230,10 +233,20 @@ mod erc721 {
#[ink(message)]
fn burn(&mut self, id: TokenId) -> Result<(), Error> {
let caller = self.env().caller();
if self.token_owner.get(&id) != Some(&caller) {
let Self {
token_owner,
owned_tokens_count,
..
} = self;
let occupied = match token_owner.entry(id) {
Entry::Vacant(_) => return Err(Error::TokenNotFound),
Entry::Occupied(occupied) => occupied,
};
if occupied.get() != &caller {
return Err(Error::NotOwner)
};
self.remove_token_from(&caller, id)?;
decrease_counter_of(owned_tokens_count, &caller)?;
occupied.remove_entry();
self.env().emit_event(Transfer {
from: Some(caller),
to: Some(AccountId::from([0x0; 32])),
......@@ -273,26 +286,37 @@ mod erc721 {
from: &AccountId,
id: TokenId,
) -> Result<(), Error> {
if !self.exists(id) {
return Err(Error::TokenNotFound)
}
self.decrease_counter_of(from)?;
self.token_owner.take(&id).ok_or(Error::CannotRemove)?;
let Self {
token_owner,
owned_tokens_count,
..
} = self;
let occupied = match token_owner.entry(id) {
Entry::Vacant(_) => return Err(Error::TokenNotFound),
Entry::Occupied(occupied) => occupied,
};
decrease_counter_of(owned_tokens_count, from)?;
occupied.remove_entry();
Ok(())
}
/// Adds the token `id` to the `to` AccountID.
fn add_token_to(&mut self, to: &AccountId, id: TokenId) -> Result<(), Error> {
if self.exists(id) {
return Err(Error::TokenExists)
let Self {
token_owner,
owned_tokens_count,
..
} = self;
let vacant_token_owner = match token_owner.entry(id) {
Entry::Vacant(vacant) => vacant,
Entry::Occupied(_) => return Err(Error::TokenExists),
};
if *to == AccountId::from([0x0; 32]) {
return Err(Error::NotAllowed)
};
self.increase_counter_of(to)?;
if self.token_owner.insert(id, *to).is_some() {
return Err(Error::CannotInsert)
}
let entry = owned_tokens_count.entry(*to);
increase_counter_of(entry)?;
vacant_token_owner.insert(*to);
Ok(())
}
......@@ -350,33 +374,6 @@ mod erc721 {
Ok(())
}
/// Increase token counter from the `of` AccountId.
fn increase_counter_of(&mut self, of: &AccountId) -> Result<(), Error> {
if self.balance_of_or_zero(of) > 0 {
let count = self
.owned_tokens_count
.get_mut(of)
.ok_or(Error::CannotFetchValue)?;
*count += 1;
Ok(())
} else {
match self.owned_tokens_count.insert(*of, 1) {
Some(_) => Err(Error::CannotInsert),
None => Ok(()),
}
}
}
/// Decrease token counter from the `of` AccountId.
fn decrease_counter_of(&mut self, of: &AccountId) -> Result<(), Error> {
let count = self
.owned_tokens_count
.get_mut(of)
.ok_or(Error::CannotFetchValue)?;
*count -= 1;
Ok(())
}
/// Removes existing approval from token `id`.
fn clear_approval(&mut self, id: TokenId) -> Result<(), Error> {
if !self.token_approvals.contains_key(&id) {
......@@ -420,12 +417,33 @@ mod erc721 {
}
}
fn decrease_counter_of(
hmap: &mut StorageHashMap<AccountId, u32>,
of: &AccountId,
) -> Result<(), Error> {
let count = (*hmap).get_mut(of).ok_or(Error::CannotFetchValue)?;
*count -= 1;
Ok(())
}
/// Increase token counter from the `of` AccountId.
fn increase_counter_of(entry: Entry<AccountId, u32>) -> Result<(), Error> {
entry.and_modify(|v| *v += 1).or_insert(1);
Ok(())
}
/// Unit tests
#[cfg(test)]
mod tests {
/// Imports all the definitions from the outer scope so we can use them here.
use super::*;
use ink_core::env;
use ink_core::{
env,
env::{
call,
test,
},
};
/// Executes the given test through the off-chain environment.
fn run_test<F>(test_fn: F)
......@@ -725,5 +743,42 @@ mod erc721 {
assert_eq!(erc721.owner_of(1), None);
})
}
#[test]
fn burn_fails_token_not_found() {
run_test(|| {
// Create a new contract instance.
let mut erc721 = Erc721::new();
// Try burning a non existent token
assert_eq!(erc721.burn(1), Err(Error::TokenNotFound));
})
}
#[test]
fn burn_fails_not_owner() {
run_test(|| {
let accounts = env::test::default_accounts::<env::DefaultEnvTypes>()
.expect("Cannot get accounts");
// Create a new contract instance.
let mut erc721 = Erc721::new();
// Create token Id 1 for Alice
assert_eq!(erc721.mint(1), Ok(()));
// Try burning this token with a different account
set_sender(accounts.eve);
assert_eq!(erc721.burn(1), Err(Error::NotOwner));
})
}
fn set_sender(sender: AccountId) {
let callee =
env::account_id::<env::DefaultEnvTypes>().unwrap_or([0x0; 32].into());
test::push_execution_context::<EnvTypes>(
sender,
callee,
1000000,
1000000,
test::CallData::new(call::Selector::new([0x00; 4])), // dummy
);
}
}
}
......@@ -355,7 +355,7 @@ mod multisig_plain {
ensure_requirement_is_valid(len, requirement);
self.owners.swap_remove(self.owner_index(&owner));
self.is_owner.take(&owner);
*self.requirement = requirement;
Lazy::set(&mut self.requirement, requirement);
self.clean_owner_confirmations(&owner);
self.env().emit_event(OwnerRemoval { owner });
}
......@@ -392,7 +392,7 @@ mod multisig_plain {
fn change_requirement(&mut self, new_requirement: u32) {
self.ensure_from_wallet();
ensure_requirement_is_valid(self.owners.len(), new_requirement);
*self.requirement = new_requirement;
Lazy::set(&mut self.requirement, new_requirement);
self.env().emit_event(RequirementChange { new_requirement });
}
......@@ -457,15 +457,14 @@ mod multisig_plain {
self.ensure_caller_is_owner();
let caller = self.env().caller();
if self.confirmations.take(&(trans_id, caller)).is_some() {
if self.confirmation_count.contains_key(&trans_id) {
if let Some(value) = self.confirmation_count.get_mut(&trans_id) {
*value -= 1;
} else {
unreachable!()
}
} else {
self.confirmation_count.insert(trans_id, 0);
}
self.confirmation_count
.entry(trans_id)
.and_modify(|v| {
if *v > 0 {
*v -= 1;
}
})
.or_insert(1);
self.env().emit_event(Revokation {
transaction: trans_id,
from: caller,
......@@ -532,13 +531,7 @@ mod multisig_plain {
confirmer: AccountId,
transaction: TransactionId,
) -> ConfirmationStatus {
if !self.confirmation_count.contains_key(&transaction) {
self.confirmation_count.insert(transaction, 0);
}
let count = self
.confirmation_count
.get_mut(&transaction)
.expect("just inserted if missing");
let count = self.confirmation_count.entry(transaction).or_insert(0);
let new_confirmation = self
.confirmations
.insert((transaction, confirmer), ())
......@@ -598,13 +591,7 @@ mod multisig_plain {
})
{
if self.confirmations.take(&(trans_id, *owner)).is_some() {
if !self.confirmation_count.contains_key(&trans_id) {
self.confirmation_count.insert(trans_id, 0);
}
*self
.confirmation_count
.get_mut(&trans_id)
.expect("just inserted if missing") += 1;
*self.confirmation_count.entry(trans_id).or_insert(0) += 1;
}
}
}
......@@ -958,6 +945,28 @@ mod multisig_plain {
assert_eq!(*contract.confirmation_count.get(&0).unwrap(), 2);
}
#[test]
fn revoke_confirmations() {
// given
let mut contract = submit_transaction();
let accounts = default_accounts();
// Confirm by Bob
set_sender(accounts.bob);
contract.confirm_transaction(0);
// Confirm by Eve
set_sender(accounts.eve);
contract.confirm_transaction(0);
assert_eq!(contract.confirmations.len(), 3);
assert_eq!(*contract.confirmation_count.get(&0).unwrap(), 3);
// Revoke from Eve
contract.revoke_confirmation(0);
assert_eq!(*contract.confirmation_count.get(&0).unwrap(), 2);
// Revoke from Bob
set_sender(accounts.bob);
contract.revoke_confirmation(0);
assert_eq!(*contract.confirmation_count.get(&0).unwrap(), 1);
}
#[test]
fn confirm_transaction_already_confirmed() {
let mut contract = submit_transaction();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment