-
Notifications
You must be signed in to change notification settings - Fork 8
Competition Ink!
Noel Kwan edited this page Sep 22, 2021
·
1 revision
Parity's ink! for smart contracts on Substrate / Polka Dot (Rust eDSL compiled to Wasm)
Pros:
- Do away with all the ad hoc languages for low-level virtual machines.
- Reuse the type system of Rust for much better contracts.
Cons:
- Ultimately, still a relatively low-level language on top of which one must build a lot of abstractions before to express adversarial properties of a DApp.
See below the Closing contract written in ink!, and notice that the number of lines of code is far greater than that of the same contract written in Glow:
use ink_env;
use ink_env::Environment;
use ink_lang as ink;
#[ink::chain_extension]
pub trait RecoverId {
type ErrorCode = SigRecoveryErr;
// See: https://paritytech.github.io/ink/ink_lang_macro/attr.chain_extension.html#attributes
// 1101 here is arbitrary, depends on implementation in substrate node.
#[ink(extension = 1101, returns_result = false)]
fn recover_id(v: u8, r: [u8; 32], s: [u8; 32]) -> ink_env::AccountId;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum SigRecoveryErr {
InvalidSignature,
}
impl ink_env::chain_extension::FromStatusCode for SigRecoveryErr {
fn from_status_code(status_code: u32) -> Result<(), Self> {
match status_code {
0 => Ok(()),
1 => Err(Self::InvalidSignature),
_ => panic!("encountered unknown status code"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum SignEnv {}
impl Environment for SignEnv {
const MAX_EVENT_TOPICS: usize = 5;
type AccountId = <ink_env::DefaultEnvironment as Environment>::AccountId;
type Balance = <ink_env::DefaultEnvironment as Environment>::Balance;
type Hash = <ink_env::DefaultEnvironment as Environment>::Hash;
type BlockNumber = <ink_env::DefaultEnvironment as Environment>::BlockNumber;
type Timestamp = <ink_env::DefaultEnvironment as Environment>::Timestamp;
type ChainExtension = RecoverId;
}
#[ink::contract(env = crate::SignEnv)]
pub mod closing {
#[ink(storage)]
pub struct Closing {
buyer: AccountId,
seller: AccountId,
digest: [u8; 32],
price: u128,
}
// Third parties can verify this for themselves.
#[ink(event)]
pub struct Signed {
#[ink(topic)]
signee: AccountId,
#[ink(topic)]
digest: [u8; 32],
#[ink(topic)]
v: u8,
#[ink(topic)]
r: [u8; 32],
#[ink(topic)]
s: [u8; 32],
}
impl Closing {
/// Buyer calls this with signed document (digest),
/// and price that should be paid to seller.
#[ink(constructor)]
pub fn new(buyer: AccountId, seller: AccountId, digest: [u8; 32], price: u128) -> Self {
Self { buyer, seller, digest, price }
}
/// Seller calls this after verifying digest for Buyer's signature,
/// and signing it.
/// This verifies and publishes the Seller's signature against the digest
/// before releasing payment.
#[ink(message)]
pub fn verify_and_withdraw(&mut self, v: u8, r: [u8; 32], s: [u8; 32]) {
let caller: AccountId = self.env().caller();
assert!(caller == self.seller);
assert!(self.price <= self.env().balance(), "Insufficient funds!");
// Verify seller signature
assert_eq!(self.seller, self.env().extension().recover_id(v, r, s).unwrap());
// Publish signature
self.env().emit_event(Signed {
signee: self.seller,
digest: self.digest,
v, r, s
});
// Seller receives their funds, terminate the contract
self.env().transfer(self.seller, self.price);
self.env().terminate_contract(self.buyer);
}
}
}