Releases: use-ink/ink
ink! 3.0.0 RC 5
Version 3.0-rc5 (2021-09-08)
This is the 5th release candidate for ink! 3.0.
The list below shows the additions, changes and fixes that are visible to users of ink!.
Compatibility
In the past we recommended using our canvas-node
for local contract development and testing. We've now migrated this node to be run as a Parachain. This new setup comes with some additional overhead though (such as requiring a local Polkadot installation); for local development this is often unnecessary.
We've therefore created a new project, the substrate-contracts-node
. It fulfills the same purpose the canvas-node
did before ‒ it's a standalone node which is just Substrate's node-template
modified to include the contracts
pallet.
You can install the newest version like this:
cargo install contracts-node --git https://github.com/paritytech/substrate-contracts-node.git --force
After you've installed the node it can be run via substrate-contracts-node --tmp --dev
.
Added
- Added example for mocking chain extensions in off-chain tests ‒ #882.
- Panic messages are now printed to debug buffer ‒ #894.
Changed
ink! 3.0.0 RC 4
Version 3.0-rc4 (2021-07-19)
This is the 4th release candidate for ink! 3.0.
The list below shows the additions, changes and fixes that are visible to users of ink!.
Compatibility
ink! 3.0-rc4 is compatible with
- The "ink! CLI"
cargo-contract
version0.13.0
or newer.- Install the newest version using
cargo install --force cargo-contract
.
- Install the newest version using
- Substrate version
4.0.0-dev
including thecontracts-pallet
version4.0.0-dev
. canvas-node
version0.19.0
or newer.- Install the newest version using
cargo install canvas-node --git https://github.com/paritytech/canvas-node.git --force
.
- Install the newest version using
The documentation on our Documentation Portal
is up to date with this release candidate. Since the last release candidate we notabley
added a number of Frequently Asked Questions
there.
Quality Assurance
In order to ensure a continuously high quality of our codebase we implemented a number
of key improvements to our testing setup:
- We've put an emphasis on automated testing of the usage examples in our crate documentation.
Those are now tested in the context of a complete ink! contract. In the past this was not
always the case, sometimes usage examples were just isolated code snippets. - We started our
ink-waterfall
project,
which runs End-to-End tests through our entire stack.
All our examples are continuously built using the latestcargo-contract
. They are
subsequently deployed on the latestcanvas-node
by emulating browser interactions with
both thecanvas-ui
and the
polkadot-js
UI.
This testing setup enables us to detect bugs which only appear in the context of using
multiple components together early on. - To improve the readability of our documentation we introduced automated grammar and spell
checking into our Continuous Integration environment.
Added
- Added support for the new
seal_random
API ‒ #734. - Added missing documentation for the
ink_storage_derive
procedural macros ‒ #711. - Implemented the (unstable)
seal_rent_params
API ‒ #755. - Implemented the (unstable)
seal_rent_status
API ‒ #798. - Implemented the (unstable)
seal_debug_message
API ‒ #792.- Printing debug messages can now be achieved via
ink_env::debug_println!(…)
. - See our documentation
for more information. - The examples have been updated to reflect this new way of printing debug messages.
- Printing debug messages can now be achieved via
- Added usage comments with code examples to the
ink_env
API ‒ #797.- The published crate documentation now contains
much more code examples for the methods behindself.env()
andSelf::env()
.
- The published crate documentation now contains
- Added an example implementation for ERC-1155, a multi-token standard ‒ #800.
- Implemented binary search for
collections::Vec
‒ #836. - Added the ability of submitting payable transactions to the
multisig
example ‒ #820. - Implemented
Decode
forError
types in the examples, enabling building them as dependencies ‒ #761. - We started working on a new off-chain environment testing engine ‒ #712.
- The old testing environment has a number of limitations, which we are well aware of.
We're confident that with the new testing engine we will be able to conduct much more
elaborate testing in an emulated chain environment. - For the moment, the new engine is unstable and only available behind a feature flag.
A number of examples have already been converted to support the new testing engine.
- The old testing environment has a number of limitations, which we are well aware of.
Changed
- To reduce a contract's space footprint we switched the default allocator to a bump allocator implementation ‒ #831.
- A couple of readme's have been reworked:
- With the stabilization of Rust 1.51 we ware able to remove the
ink-unstable
feature, making
collections::SmallVec
andlazy::LazyArray
available by default ‒ #746. - To resolve confusion, we migrated all usages of
#[test]
in our examples to#[ink::test]
‒ #746.- The difference is that
#[ink::test]
spawns an emulated chain environment (an "off-chain" environment)
and hence comes with a bit of overhead. It was not always clear to users when they require
an off-chain environment, we decided to mitigate this confusion by using an emulated chain
environment for all our example tests.
- The difference is that
- With the stabilization of Rust's
min_const_generics
we were able to replace the fixed
size implementations ofSpreadLayout
andPackedLayout
for Arrays. These traits are
now implemented for all Arrays of sizeusize
‒ #754. - We were able to remove the pinned
funty
dependency ‒ #711. - The
contract-transfer
example has been improved for better UI support ‒ #789. - The
contract-transfer
example has been improved for better error handling ‒ #790.
Fixed
- Catch illegal
struct
destructuring pattern in ink! message arguments ‒ #846. - Removed an erroneous
Salt
type in code generation for cross-contract calls ‒ #842. - Do not generate metadata if compiled as dependency ‒ #811.
- Fix execution context parameters in DNS example tests ‒ #723.
- Fixed the
Greeter
contract example from our doc comments ‒ #773.
ink! 3.0.0 RC 3
Version 3.0-rc3 (2021-03-02)
This is the 3rd release candidate for ink! 3.0.
The list below shows the additions, changes and fixes that are visible to users of ink!.
Compatibility
ink! 3.0-rc3 is compatible with
- The
cargo-contract
CLI tool version0.9.1
or newer.- Install newest version using
cargo install --force cargo-contract
.
- Install newest version using
- Substrate version
3.0
including thecontracts-pallet
version3.0
.
Added
- Implemented chain extensions feature for ink!.
- ink!'s official documentation portal: https://paritytech.github.io/ink-docs/
- It is now possible to pass a
salt
argument to contract instantiations. - Implemented fuzz testing for the ink! codebase.
Changed
- Migrate
ink_storage::SmallVec
andink_storage::lazy::SmallLazyArray
to usemin_const_generics
.- The
min_const_generics
feature is going to be stabilized in Rust 1.51. For now it was put behind
theink-unstable
crate feature of theink_storage
crate.
- The
- Improve error reporting for conflicting ink! attributes.
- Improve error reporting for invalid constructor or message selector. (#561)
- Remove
iter_mut
forink_storage::BinaryHeap
data structure. - Add documented demonstration how to properly mock
transferred_balance
calls: #555 - Add contract example which uses
ext_transfer
andext_terminate
: #554 - Improve documentation of
transfer
andminimum_balance
APIs: #540
Fixed
- The Delegator example contract now compiles properly using the
build-all.sh
bash script. - Update crate dependencies:
scale-info 0.6
parity-scale-codec 2.0
rand 0.8
itertools 0.10
- Remove unused
tiny-keccak
dependency fromink_primitives
. - Changed the default
BlockNumber
type tou32
. This is a fix since it now properly mirrors Substrate's defaultBlockNumber
type. - Ensure topics are unique: #594
- Several fixes for
ink_storage
data structures, including:
ink! 3.0.0 RC 2
Version 3.0-rc2 (2020-10-22)
This is the 2nd release candidate for ink! 3.0.
On top of the changes introduced in the first release candidate for ink! 3.0 we introduced
the following improvements, new features and bug fixes:
- The
ink_storage
crate now comes with a newBinaryHeap
data structure
that has a very similar interface to the well known Rust standard library
BinaryHeap
. It features specific optimizations to reduce the storage reads
and writes required for its operations. - Fixed a bug with
ink_storage::Lazy
that corrupted the storage of
other storage data structures if it was unused in a contract execution. - The
ink_storage::alloc::Box
type now implementsscale_info::TypeInfo
which
now allows it to be fully used inside other storage data structures such as
ink_storage::collections::Vec
. The missing of this implementation was
considered a bug. - The
LazyHashMap
low-level storage abstraction is now re-exported from within
theink_storage::lazy
module and docs are inlined. - Added note about the
ink_core
split intoink_env
andink_storage
crates
to the release notes of ink! 3.0-rc1. - The
Cargo.toml
documentation now properly links to the one deployed at docs.rs.
On top of that crate level documentation for theink_allocator
crate has been
added. - Add new ERC-20 example contract based on a trait implementation. Also modernized
the old non-trait based ERC-20 example token contract.
ink! 3.0.0 RC 1
Version 3.0 (2020-10-09)
Be prepared for the ink! 3.0 release notes because the whole version was basically a rewrite of
all the major components that make up ink!. With our experience gained from previous releases
of ink! we were able to detect weak spots of the design and provided ink! with more tools,
more features and more efficiency as ever. Read more below …
Just. Be. Rust. 3.0
In the 3.0 update we further explored the space for ink! to just feel like it was plain Rust.
With this in mind we changed the syntax slightly in order to better map from ink! to the generated
Rust code. So what users see is mostly what will be generated by ink! later.
In this vein #[ink(storage)]
and #[ink(event)]
structs as well as #[ink(message)]
and
#[ink(constructor)]
methods now need to be specified with public visibility (pub
).
The #[ink(constructor)]
syntax also changes and no longer uses a &mut self
receiver but
now follows the natural Rust constructors scheme. So it is no longer possible to shoot
yourself in the foot by accidentally forgetting to initialize some important data structures.
Old ink! 2.0:
#[ink(constructor)]
fn new_erc20(&mut self, initial_supply: Balance) {
let caller = self.env().caller();
self.total_supply.set(initial_supply);
self.balances.insert(caller, initial_supply);
}
New ink! 3.0:
#[ink(constructor)]
pub fn new_erc20(initial_supply: Balance) -> Self {
let caller = self.env().caller();
let mut balances = ink_storage::HashMap::new();
balances.insert(caller, initial_supply);
Self {
total_supply: initial_supply,
balances,
}
}
Also ink! 3.0 no longer requires a mandatory version
field in the header of the ink! module attribute.
Syntactically this is all it takes to port your current ink! smart contracts over to ink! 3.0 syntax.
New Storage Module
The storage module has been reworked entirely.
Also it no longer lives in the ink_core
crate but instead is defined as its own ink_storage
crate.
In a sense it acts as the standard storage library for ink! smart contracts in that it provides all the
necessary tools and data structures to organize and operate the contract's storage intuitively and efficiently.
Lazy
The most fundamental change in how you should think about data structures provided by the new ink_storage
crate is that they are inherently lazy. We will explain what this means below!
The ink_storage
crate provides high-level and low-level lazy data structures.
The difference between high-level and low-level lies in the distinction in how these data structures are aware
of the elements that they operate on. For high-level data structures they are fully aware about the elements
they contains, do all the clean-up by themselves so the user can concentrate on the business logic.
For low-level data structures the responsibility about the elements lies in the hands of the contract author.
Also they operate on cells (Option<T>
) instead of entities of type T
.
But what does that mean exactly?
The new ink_storage::Lazy
type is what corresponds the most to the old ink_core::storage::Value
type. Both cache their entities and both act lazily on the storage. This means that a read or write operation is only performed when it really needs to in order to satisfy other inputs.
Data types such as Rust primitives i32
or Rust's very own Vec
or data structures can also be used to operate on the contract's storage, however, they will load their contents eagerly which is often not what you want.
An example follows with the below contract storage and a message that operates on either of the two fields.
#[ink(storage)]
pub struct TwoValues {
offset: i32,
a: i32,
b: i32,
}
impl TwoValues {
#[ink(message)]
pub fn set(&mut self, which: bool, new_value: i32) {
match which {
true => { self.a = self.offset + new_value; },
false => { self.b = self.offset + new_value; },
}
}
}
Whenever we call TwoValues::set
always both a
and b
are loaded despite the fact the we only operate on one of them at a time. This is very costly since storage accesses are in fact database look-ups.
In order to prevent this eager loading of storage contents we can make use of ink_storage::Lazy
or other lazy data structures defined in that crate:
#[ink(storage)]
pub struct TwoValues {
offset: i32,
a: ink_storage::Lazy<i32>,
b: ink_storage::Lazy<i32>,
}
impl TwoValues {
#[ink(message)]
pub fn set(&mut self, which: bool, new_value: i32) {
match which {
true => { self.a = offset + new_value; },
false => { self.b = offset + new_value; },
}
}
}
Now a
and b
are only loaded when the contract really needs their values.
Note that offset
remained i32
since it is always needed and could spare the minor overhead of the ink_storage::Lazy
wrapper.
HashMap
In the follow we explore the differences between the high-level ink_storage::collections::HashMap
and the low-level ink_storage::lazy::LazyHashMap
. Both provide very similar functionality in that they map some generic key to some storage entity.
However, their APIs look very different. Whereas the HashMap
provides a rich and high-level API that is comparable to that of Rust's very own HashMap
, the LazyHashMap
provides only a fraction of the API and also operates on Option<T>
values types instead of T
directly. It is more similar Solidity mappings than to Rust's HashMap
.
The fundamental difference of both data structures is that HashMap
is aware of the keys that have been stored in it and thus can reconstruct exactly which elements and storage regions apply to it. This enables it to provide iteration and automated deletion as well as efficient way to defragment its underlying storage to free some storage space again. This goes very well in the vein of Substrate's storage rent model where contracts have to pay for the storage they are using.
Data Structure | level of abstraction | caching | lazy | element type | container |
---|---|---|---|---|---|
T |
- | yes | no | T |
primitive value |
Lazy<T> |
high-level | yes | yes | T |
single element container |
LazyCell<T> |
low-level | yes | yes | Option<T> |
single element, no container |
Vec<T> |
high-level | yes | yes | T |
Rust vector-like container |
LazyIndexMap<T> |
low-level | yes | yes | Option<T> |
similar to Solidity mapping |
HashMap<K, V> |
high-level | yes | yes | V (key type K ) |
Rust map-like container |
LazyHashMap<K, V> |
low-level | yes | yes | Option<V> (key type K ) |
similar to Solidity mapping |
There are many more! For more information about the specifics please take a look into the ink_storage
crate documentation.
Spread & Packed Modes
Storing or loading complex data structures to and from contract storage can be done in many different ways. You could store all information into a single storage cell or you could try to store all information into as many different cells as possible. Both strategies have pros and cons under different conditions.
For example it might be a very good idea to store all the information under the same cell if all the information is very compact. For example when we are dealing with a byte vector that is expected to never be larger than approx a thousand elements it would probably be more efficient if we store all those thousand bytes in the same cell and especially if we often access many of those (or all) in our contract messages.
On the other hand spreading information across as many cells as possible might be much more efficient if we are dealing with big data structures, a lot of information that is not compact, or when messages that operate on the data always only need a small fraction of the whole data.
An example for this use case is if you have a vector of user accounts where each account stores potentially a lot of information, e.g. a 32-byte hash etc and where our messages only every operate on only a few of those at a time.
The ink_storage
crate provides the user full control over the strategy or a mix of these two root strategies through some fundamental abstractions that we are briefly presenting to you.
Default: Spreading Mode
By default ink! spreads information to as many cells as possible. For example if you have the following #[ink(storage)]
struct every field will live in its own single storage cell. Note that for c
all 32 bytes will share the same cell!
#[ink(storage)]
pub struct Spreaded {
a: i32,
b: ink_storage::Lazy<i32>,
c: [u8; 32],
}
Packing Storage
We can alter this behaviour by using the ink_storage::Pack
abstraction:
pub struct Spreaded {
a: i32,
b: ink_storage::Lazy<i32>,
c: [u8; 32],
}
#[ink(storage)]
pub struct Packed {
packed: ink_storage::Pack<Spreaded>,
}
Now all fields of Spreaded
will share the same storage cell. This means whenever one of them is stored to or loaded from the contract storage, all of them are stored or loaded. A user has to choose wisely what mode of operation is more suitable for their contract.
These abstractions can be combined in various ways, yielding full control to the users. For example, in the following only a
and b
share a common storage cell while c
lives in its own:
pub struct Spreaded {
a: i32,
b: ink_storage::Lazy<i32>,
}
#[ink(storage)]
pub struct Packed {
packed: ink_storage::Pack<Spreaded>,
c: [u8; 32],
}
Spreading Array Cells
If we prefer to store all bytes of c
into their own storage cell we can mak...
ink! v2.1.0
Release Notes
- Add built-in support for cryptographic hashes
- Blake2 with 128-bit and 256-bit
- Sha2 with 256-bit
- Keccak with 256-bit
- Add
ink_core::hash
module for high-level API to the new built-in hashes - Update
runtime-storage
example contract to demonstrate the new built-in hashes
ink! 2.0.0
Initial release of ink! v2.0.0.
For more information about writing ink! smart contract visit the Parity DevHub.