Skip to content

Commit

Permalink
Create the Mother of All Contracts (#1245)
Browse files Browse the repository at this point in the history
* first iteration of the BIOC

* clippy fixes

* suppress unused var warning for debug fn

* suppress unused var warning 2nd try

* + tests

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* renamed to `mother`; added storage

* refactor

* added a Mapping into contract storage

* default constructor

* reverted fix

* fix

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* make it build (but not instantiate)

* fmt

* Update examples/mother/lib.rs

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* reverted constructor delegation to satisfy CI (until #1259 fixed)

* added required input types

* custom event added

* CI clippy satisfaction

* trick to satisfy the CI until #1258 is fixed

* Revert "trick to satisfy the CI until #1258 is fixed"

This reverts commit 3491cfd.

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
  • Loading branch information
agryaznov and athei committed May 18, 2022
1 parent b6dd935 commit 8ffb7c0
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 0 deletions.
12 changes: 12 additions & 0 deletions examples/mother/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock

*~
#*#
38 changes: 38 additions & 0 deletions examples/mother/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "mother"
description = "Mother of all contracts"
version = "3.0.1"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
publish = false

[dependencies]
ink_primitives = { path = "../../crates/primitives", default-features = false }
ink_metadata = { path = "../../crates/metadata", default-features = false, features = ["derive"], optional = true }
ink_env = { path = "../../crates/env", default-features = false }
ink_storage = { path = "../../crates/storage", default-features = false }
ink_lang = { path = "../../crates/lang", default-features = false }
ink_prelude = { path = "../../crates/prelude", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }

[lib]
name = "mother"
path = "lib.rs"
crate-type = [
# Used for normal contract Wasm blobs.
"cdylib",
]

[features]
default = ["std"]
std = [
"ink_metadata/std",
"ink_env/std",
"ink_storage/std",
"ink_primitives/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
258 changes: 258 additions & 0 deletions examples/mother/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
//! # Mother of All Contracts
//!
//! This contracts is intended to make use of all features that are observable
//! by off chain tooling (for example UIs). It doesn't do anything useful beyond
//! serving off chain tooling developers with a contract to test their software against.
//! Currently, this includes the following:
//!
//! 1. Use complicated nested input and ouput types.
//! This is done through the real use case example of data structure
//! needed to store a candle auction data.
//! 2. Make contract fail with `ContractTrapped`.
//! 3. Make contract fail with returning an Error.
//! 4. Perform debug printing from contract into node's log.
//! 5. Use complicated types in storage.

#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]
mod mother {
use ink_prelude::{
string::{
String,
ToString,
},
vec::Vec,
};

use ink_lang::utils::initialize_contract;
use ink_storage::{
traits::{
PackedLayout,
SpreadAllocate,
SpreadLayout,
},
Mapping,
};

use ink_storage::traits::KeyPtr;
/// Struct for storing winning bids per bidding sample (a block).
/// Vector index corresponds to sample number.
/// Wrapping vector just added for testing UI components.
#[derive(
Default,
scale::Encode,
scale::Decode,
PartialEq,
Debug,
Clone,
SpreadLayout,
PackedLayout,
SpreadAllocate,
)]
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,)
)]
pub struct Bids(Vec<Vec<Option<(AccountId, Balance)>>>);

/// Auction outline.
#[derive(
scale::Encode, scale::Decode, PartialEq, Debug, Clone, SpreadLayout, PackedLayout,
)]
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,)
)]
pub enum Outline {
NoWinner,
WinnerDetected,
PayoutCompleted,
}

/// Auction statuses.
/// Logic inspired by
/// [Parachain Auction](https://github.com/paritytech/polkadot/blob/master/runtime/common/src/traits.rs#L160)
#[derive(
scale::Encode, scale::Decode, PartialEq, Debug, Clone, SpreadLayout, PackedLayout,
)]
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,)
)]
pub enum Status {
/// An auction has not started yet.
NotStarted,
/// We are in the starting period of the auction, collecting initial bids.
OpeningPeriod,
/// We are in the ending period of the auction, where we are taking snapshots of the winning
/// bids. Snapshots are taken currently on per-block basis, but this logic could be later evolve
/// to take snapshots of on arbitrary length (in blocks)
EndingPeriod(BlockNumber),
/// Candle was blown
Ended(Outline),
/// We have completed the bidding process and are waiting for the Random Function to return some acceptable
/// randomness to select the winner. The number represents how many blocks we have been waiting.
RfDelay(BlockNumber),
}

impl SpreadAllocate for Status {
#[inline]
fn allocate_spread(ptr: &mut KeyPtr) -> Self {
ptr.advance_by(<BlockNumber>::FOOTPRINT * 2);
Self::NotStarted
}
}
/// Struct for storing auction data.
#[derive(
Debug,
PartialEq,
scale::Encode,
scale::Decode,
Clone,
SpreadLayout,
PackedLayout,
SpreadAllocate,
)]
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,)
)]
pub struct Auction {
/// Branded name of the auction event
name: String,
/// Some hash identifiyng the auction subject
subject: Hash,
/// Structure storing the bids being made
bids: Bids,
/// Auction terms encoded as:
/// [start_block, opening_period, closing_period]
terms: [BlockNumber; 3],
/// Auction status
status: Status,
/// Candle auction can have no winner.
/// If auction is finalized, that means that the winner is determined.
finalized: bool,
/// Just a vector for UI tests
vector: Vec<u8>,
}

impl Default for Auction {
fn default() -> Auction {
Auction {
name: String::default(),
subject: Hash::default(),
bids: Bids::default(),
terms: <[BlockNumber; 3]>::default(),
status: Status::OpeningPeriod,
finalized: false,
vector: <Vec<u8>>::default(),
}
}
}

/// Way to fail a contract execution.
#[derive(scale::Encode, scale::Decode, Debug, PartialEq)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum Failure {
Revert(String),
Panic,
}

/// Event emitted when an auction being echoed.
#[ink(event)]
pub struct AuctionEchoed {
auction: Auction,
}

/// Storage of the contract.
#[ink(storage)]
#[derive(Default, SpreadAllocate)]
pub struct Mother {
auction: Auction,
balances: Mapping<AccountId, Balance>,
}

impl Mother {
#[ink(constructor)]
pub fn new(auction: Auction) -> Self {
initialize_contract(|c: &mut Self| {
c.balances = <Mapping<AccountId, Balance>>::default();
c.auction = auction;
})
}

#[ink(constructor)]
pub fn default() -> Self {
initialize_contract(|c: &mut Self| {
c.balances = <Mapping<AccountId, Balance>>::default();
c.auction = Auction::default();
})
}

/// Takes an auction data struct as input and returns it back.
#[ink(message)]
pub fn echo_auction(&mut self, auction: Auction) -> Auction {
self.env().emit_event(AuctionEchoed {
auction: auction.clone(),
});
auction
}

/// Fails contract execution in the required way.
#[ink(message)]
pub fn revert_or_trap(&mut self, fail: Option<Failure>) -> Result<(), Failure> {
match fail {
Some(Failure::Revert(_)) => {
Err(Failure::Revert("Reverting on user demand!".to_string()))
}
Some(Failure::Panic) => {
panic!("Trapping on user demand!")
}
None => Ok(()),
}
}

/// Prints the specified string into node's debug log.
#[ink(message)]
pub fn debug_log(&mut self, message: String) {
ink_env::debug_println!("debug_log: {}", message);
}
}

#[cfg(test)]
mod tests {
use super::*;
use ink_lang as ink;

#[ink::test]
fn echo_auction_works() {
let auction = Auction::default();
let mut contract = Mother::default();
assert_eq!(contract.echo_auction(auction.clone()), auction);
}

#[ink::test]
fn revert_works() {
let mut contract = Mother::default();
assert_eq!(
contract.revert_or_trap(Some(Failure::Revert(
"Testing reverting on demand!".to_string()
))),
Err(Failure::Revert("Reverting on user demand!".to_string()))
);
contract
.revert_or_trap(None)
.expect("Contract unexpected failure!");
}

#[ink::test]
#[should_panic]
fn trap_works() {
let mut contract = Mother::default();
let _ = contract.revert_or_trap(Some(Failure::Panic));
}
}
}

0 comments on commit 8ffb7c0

Please sign in to comment.