Updated documentation.

This commit is contained in:
ricmoo 2017-04-04 18:43:41 -04:00
parent 9fa2c878b9
commit e746891ae0
10 changed files with 267 additions and 97 deletions

View File

@ -39,7 +39,7 @@ Prototype
---------
:sup:`prototype` **. privateKey**
The hex string private key for this node.
The :ref:`hex string <hexstring>` private key for this node.
:sup:`prototype` **. publicKey**
The (compressed) public key for this node.
@ -133,7 +133,7 @@ Static Methods
--------------
:sup:`Interface` . encodeParams( types , values )
Returns a hex string of the *values* encoded as the *types*. (throws if a
Returns a :ref:`hex string <hexstring>` of the *values* encoded as the *types*. (throws if a
value is invalid for the type)
:sup:`Interface` . decodeParams( [ names , ] types , data )
@ -419,7 +419,7 @@ The SigningKey interface provides an abstraction around the
Creating Instances
------------------
A private key may be a any hex string or an `Arrayish`_
A private key may be a any :ref:`hex string <hexstring>` or an :ref:`Arrayish <api-arrayish>`
representing 32 bytes.
new :sup:`ethers` . _SigningKey ( privateKey )
@ -446,13 +446,17 @@ Static Methods
--------------
:sup:`_SigningKey` . recover( digest, r, s, recoveryParam )
foobar
Given a message *digest* and the signature parameters *r*, *s*
and *recoveryParam* compute the the address that signed the
message.
:sup:`_SigningKey` . getPublicKey( value [, compressed] )
foobar
:sup:`_SigningKey` . getPublicKey( publicOrPrivateKey [, compressed] )
Given a *publicOrPrivateKey*, return the public key, optionally *compressed*.
:sup:`_SigningKey` . publicKeyToAddress( publicKey )
foobar
**default:** *compressed*\ =false
:sup:`_SigningKey` . publicKeyToAddress( publicOrPrivateKey )
Convert a *publicOrPrivateKey* to an Ethereum address.
*Examples*
@ -520,8 +524,8 @@ This encoding method is used internally for several aspects of Ethereum, such as
encoding transactions and determining contract addresses. For most developers this
should not be necessary to use.
RLP can encode nested arrays, with data as hex strings and Uint8Array (or other non-Array
arrayish objects). A decoded object will always have data represented as hex strings and
RLP can encode nested arrays, with data as :ref:`hex strings <hexstring>` and Uint8Array (or other non-Array
:ref:`arrayish <api-arrayish>` objects). A decoded object will always have data represented as :ref:`hex strings <hexstring>` and
Arrays.
See: https://github.com/ethereum/wiki/wiki/RLP
@ -530,7 +534,7 @@ Static Methods
--------------
:sup:`RLP` . encode( object )
Encodes an object as an RLP hex string. (throws an Error if the object contains
Encodes an object as an RLP :ref:`hex string <hexstring>`. (throws an Error if the object contains
invalid items)
:sup:`RLP` . decode( hexStringOrArrayish )
@ -558,3 +562,5 @@ Static Methods
.. _BIP 32 Specification: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
.. _BIP 39 Specification: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
.. EOF

View File

@ -3,38 +3,30 @@
Contracts
*********
What are contracts... The preferred way to interact with code liivng on the
blockchain, but require converting
This API provides a graceful connection to a contract deployed on the blockchain,
simplifying calling and querying its functions and handling all the binary
protocol and conversion as necessarily.
Contracts consist of two kinds of functions that can be called
The Contract object is a meta-class, so many of the functions it will have are
not defined until it is instantiated with an application binary interface (ABI)
which is usually generated by a compiler, such as Solidity.
Synchronous
A contract function which is marked as **constant** is *free* and does
not affect the blockchain, and can return a value back to your code
(via a promise, since communicating to the network is still
asynchronous)
To better see this demonstrated, see the `example`_ below.
Asynchronous
Any function which modifies the state of a contract costs *ether* be
sent in a transaction, and requires wiating for a block to be mined for
that transaction. These functions cannot return any values back to
JavaScript (although the function in the contract may return a value, that
value is only available to contracts calling that function)
Most of a contract is procedurally defined by the ABI provided, but in addition
to those functions, there are two properties which can (and ofter should) be set:
::
var Contract = ethers.Contract;
-----
Connecting to a Contract
========================
new `ethers` . Contract ( address , interface , providerOrSigner )
new :sup:`ethers` . Contract ( address , interface , providerOrSigner )
Connects to the contract at *address* defined by *interface*, which
may be a JSON string or the parsed object.
The *providerOrSigner* may be any instance the following:
The *providerOrSigner* may be any instance of the following:
:ref:`Wallet <api-wallet>`
The wallet will be used to sign and send transactions, and
@ -56,14 +48,22 @@ Prototype
The prototype will contain all the methods and events defined in the
**interface**.
Name collisions with the following properties will not overwrite them.
The result of all contant methods are a :ref:`Promise <promise>` which
resolve to the result as a tuple, optionally with the parameters
accessible by name, if named in the ABI.
The result of all non-constant methods are a :ref:`Promise <promise>`
which resolve to the :ref:`transaction <transactionrequest>` that
was sent to the network.
Name collisions with the built-in properties (below) will not be overwritten.
Instead, they must be accessed through the **functions** or **events**
property.
Due to signature overloading, multiple functions can have the same name.
The JavaScript type system cannot determine these, so only the first
function with a given name will be available. (In the future this will
be addressed).
be addressed by adding parameter explicit calls).
:sup:`prototype` . address
The address of the contract.
@ -81,10 +81,12 @@ be addressed).
An object that maps each ABI function name to a function that will
estimate the cost the provided parameters.
:sup:`prototype` . events . on\ *functionname*
:sup:`prototype` . events . on\ *eventname*
An object that maps each ABI event name (lower case, with the "on"
prefix) to a callback that is triggered when the event occurs.
.. _example:
Examples
--------
@ -145,7 +147,7 @@ Examples
var contract = new ethers.Contract(address, abi, provider);
*Example Constant Function* ::
*Example Constant Function* -- **getValue ( ) returns ( address author , string value )** ::
var callPromise = contract.getValue();
@ -167,16 +169,7 @@ Examples
// var callPromise = contract.functions.getValue();
*Example Non-Constant Gas Estimate* ::
var estimatePromise = contract.estimate.setValue("Hello World");
estimatePromise.then(function(gasCost) {
// gasCost is returned as BigNumber
console.log('Estimated Gas Cost: ' + gasCost.toString());
});
*Example Non-Constant Function* ::
*Example Non-Constant Function* -- **setValue ( string value )** ::
var sendPromise = contract.setValue("Hello World");
@ -188,7 +181,7 @@ Examples
// var sendPromise = contract.functions.setValue("Hello World");
*Example Event Registration* ::
*Example Event Registration* -- **valueChanged ( author , value )** ::
// Register for events
contract.onvaluechanged = function(author, value) {
@ -199,6 +192,16 @@ Examples
// This is identical to the above event registry
// contract.events.onvaluechanged = function(authot, value) { ...
*Example Non-Constant Gas Estimate* ::
var estimatePromise = contract.estimate.setValue("Hello World");
estimatePromise.then(function(gasCost) {
// gasCost is returned as BigNumber
console.log('Estimated Gas Cost: ' + gasCost.toString());
});
-----
Result Types
@ -211,10 +214,10 @@ and returning values in Contracts.
Integers
--------
Integers in solidity are a fixed number of bits (aligned to the nearest bytes)
Integers in solidity are a fixed number of bits (aligned to the nearest byte)
and are available in signed and unsigned variants.
For example, a **uint256** is 256 bits (32 bytes) and unsigned. An *int8*
For example, a **uint256** is 256 bits (32 bytes) and unsigned. An **int8**
is 8 bits (1 byte) and signed.
When the type is 48 bits (6 bytes) or less, values are returned as a JavaScript
@ -227,7 +230,8 @@ When passing numeric values in, JavaScript Numbers, hex strings or any BigNumber
is acceptable (however, take care when using JavaScript Numbers amd performing
mathematic operations on them).
The uint and int types are aliases for uint256 and int256, respectively.
The **uint** and **int** types are aliases for **uint256** and **int256**,
respectively.
Strings
-------
@ -333,8 +337,8 @@ Solidity compiler.
-----
Custom Signers
==============
Custom Signer
=============
The simplest way to specify a signer is to simply use an instance of a wallet.
However, if more fine-grained control is required, a custom signer allow
@ -343,30 +347,43 @@ deferring the address, signing and sending transactions.
A signer can be any object with:
:sup:`object` . getAddress()
Which must return a vaid address or a promise which will resolve to a valid
*Required.*
Which must return a valid address or a :ref:`Promise <promise>` which will resolve to a valid
address or reject an error.
:sup:`object` . provider
*Required.*
A provider that will be used to connect to the Ethereum blockchain to issue
calls, listen for events and possibly send transaction.
:sup:`object` . estimateGas ( transaction )
*Optional.*
If this is not defined, the provider is queries directly, after populating
the address using *getAddress()*.
The result must be a :ref:`Promise <promise>` which resolves to the
:ref:`BigNumber <bignumber>` estimated gas cost.
:sup:`object` . sendTransaction ( transaction )
*Optional.*
If this is defined, it is called instead of sign and is expected to
populate *nonce*, *gasLimit* and *gasPrice*.
The result must be a `Promise`_ which resolves to the sent transaction, or
The result must be a :ref:`Promise <promise>` which resolves to the sent transaction, or
rejects on failure.
:sup:`instance` . sign ( transaction )
:sup:`object` . sign ( transaction )
*Optional.*
If this is defined, it is called to sign a transaction before using the
provider to send it to the network.
The result may be a valid hexString or a promise which will resolve to a valid
hexString signed transaction or reject on failure.
The result may be a valid :ref:`hex string <hexstring>` or a promise which will resolve to a valid
:ref:`hex string <hexstring>` signed transaction or reject on failure.
*Examples*
----------
@ -410,4 +427,4 @@ A signer can be any object with:
-----
.. Hello World
.. EOF

View File

@ -104,6 +104,11 @@ JsonRpcProvider :sup:`( inherits from Provider )`
:sup:`prototype` . url
The JSON-RPC URL the provider is connected to
:sup:`prototype` . send ( method , params )
Send the JSON-RPC *method* with *params*. This is useful for calling
non-standard or less common JSON-RPC methods. A :ref:`Promise <promise>` is
returned which will resolve to the parsed JSON result.
EtherscanProvider :sup:`( inherits from Provider )`
---------------------------------------------------
@ -234,7 +239,7 @@ usually be used instead.
:sup:`prototype` . call ( transaction )
Send the **read-only** (constant) *transaction* to a single Ethereum node and
return a :ref:`Promise <promise>` with the result (as a hex string) of executing it.
return a :ref:`Promise <promise>` with the result (as a :ref:`hex string <hexstring>`) of executing it.
(See `Transaction Requests <transactionrequest>`_)
This is free, since it does not change any state on the blockchain.
@ -259,7 +264,7 @@ usually be used instead.
::
foobar
@TODO
-----
@ -267,10 +272,10 @@ Contract State
==============
:sup:`prototype` . getCode ( address )
Returns a :ref:`Promise <promise>` with the bytecode (as a hex string) at *address*.
Returns a :ref:`Promise <promise>` with the bytecode (as a :ref:`hex string <hexstring>`) at *address*.
:sup:`prototype` . getStorageAt ( address, position [ , blockTag ] )
Returns a :ref:`Promise <promise>` with the value (as a hex string) at *address* in
Returns a :ref:`Promise <promise>` with the value (as a :ref:`hex string <hexstring>`) at *address* in
*position* at *blockTag*. (See `Block Tags <blocktag>`_)
default: *blockTag*\ = "latest"
@ -284,7 +289,7 @@ Contract State
::
foobar
@TODO
-----
@ -337,7 +342,7 @@ Waiting for Transactions
------------------------
:sup:`prototype` . waitForTransaction ( transachtionHash [ , timeout ] )
Return a Promise which returns the transaction once *transactionHash* is
Return a :ref:`Promise <promise>` which returns the transaction once *transactionHash* is
mined, with an optional *timeout* (in milliseconds)
*Examples*
@ -383,7 +388,7 @@ Block Tag
A block tag is used to uniquely identify a block's position in th blockchain:
a Number or hex string:
a Number or :ref:`hex string <hexstring>`:
Each block has a block number (eg. ``42`` or ``"0x2a``.
"latest":
@ -427,7 +432,7 @@ Transaction Requests
--------------------
Any property which accepts a number may also be specified as a :ref:`BigNumber <bignumber>`
or hex string.
or :ref:`hex string <hexstring>`.
::
@ -579,3 +584,5 @@ Provider Specific Extra API Calls
.. _events: https://nodejs.org/dist/latest-v6.x/docs/api/events.html
.. _replay-attack: https://github.com/ethereum/EIPs/issues/155
.. _api-topics: https://github.com/ethereum/wiki/wiki/JSON-RPC#a-note-on-specifying-topic-filters
.. EOF

View File

@ -75,8 +75,9 @@ Creating Instances
**examples:** utils.bigNumberify("42")
*Hex String*
A string with a **prefix of 0x** and consisting of the hexidecimal digits 0 through 9 and
a through f, case-insensitive. Must be non-negative.
A :ref:`hex string <hexstring>`, witch has aa **prefix of 0x** and consisting
of the hexidecimal digits 0 through 9 and a through f, case-insensitive. Must
be non-negative.
**examples:** utils.bigNumberify("0x2a")
@ -86,7 +87,7 @@ Creating Instances
**examples:** utils.bigNumberify(42)
*Arrayish*
Treats the :ref:`arrayish <arrayish>` as a big-endian encoded bytes representation.
Treats the :ref:`arrayish <api-arrayish>` as a big-endian encoded bytes representation.
**examples:** utils.bigNumberify([ 42 ])
@ -310,13 +311,15 @@ An arrayish object is any such that it:
-----
.. _api-hexstring:
.. _hexstring:
Hex Strings
===========
A hex string is **always** prefixed with "0x" and is always returned
with even-length (although any hex string may be passed in with odd-length).
A hex string is **always** prefixed with "0x" and consists of the characters
0 -- 9 and a -- f. It is always returned lower case with even-length, but any hex
string passed into a function may be any case and may be odd-length.
:sup:`utils` . hexlify ( numberOrBigNumberOrHexStringOrArrayish )
Converts any number, :ref:`BigNumber <bignumber>`, hex string or
@ -353,4 +356,4 @@ of the transaction.
-----
\
.. EOF

View File

@ -3,7 +3,7 @@
Wallets
*******
A **Wallet** manages a private/public key pair which is used to cryptographically sign
A **wallet** manages a private/public key pair which is used to cryptographically sign
transactions and prove ownership on the Ethereum network.
::
@ -127,7 +127,7 @@ Prototype
`address`_ property
:sup:`prototype` . sign ( transaction )
Signs *transaction* and returns the signed transaction as a raw hex string.
Signs *transaction* and returns the signed transaction as a :ref:`hex string <hexstring>`.
See :ref:`Transaction Requests <transactionrequest>`.
:sup:`prototype` . encrypt ( password [ , options ] [ , progressCallback ] )
@ -225,6 +225,10 @@ These operations require the wallet have a provider attached to it.
**Query the Network** ::
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
var wallet = new ethers.Wallet(privateKey);
wallet.provider = ethers.providers.getDefaultProvider();
var balancePromise = wallet.getBalance(address);
balancePromise.then(function(balance) {
@ -241,6 +245,10 @@ These operations require the wallet have a provider attached to it.
**Transfer Ether** ::
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
var wallet = new ethers.Wallet(privateKey);
wallet.provider = ethers.providers.getDefaultProvider();
// We must pass in the amount as wei (1 ether = 1e18 wei), so we use
// this convenience function to convert ether to wei.
var amount = ethers.parseEther('1.0');
@ -267,6 +275,10 @@ These operations require the wallet have a provider attached to it.
**Sending (Complex) Transactions** ::
var privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
var wallet = new ethers.Wallet(privateKey);
wallet.provider = ethers.providers.getDefaultProvider();
var transaction = {
// Recommendation: omit nonce; the provider will query the network
// nonce: 0,
@ -351,4 +363,4 @@ Parsing Transactions
-----
\
.. EOF

View File

@ -3,6 +3,11 @@ Cookbook
Some quick snippets of code and ideas to work from.
Some of these recipes are stubs that will be filled in shortly.
If there is a simple recipe you would like added, please send
suggestions to support@ethers.io.
-----
Dump Balances of All Geth Wallets (in the current director)
@ -30,7 +35,7 @@ Parity
return;
}
var address = Wallet.getWalletAddress(data.toString());
var address = JSON.parse(data.toString()).address;
provider.getBalance(address).then(function(balance) {
console.log(address + ':' + ethers.formatEther(balance));
});
@ -58,9 +63,15 @@ Include example links to etherscan showing the transactions
Promise.all([
wallet.getBalance(),
provider.getGasPrice(),
provider.getCode(newAddress)
]).then(function(results) {
var balance = results[0];
var gasPrice = results[1];
var code = results[2];
if (code !== '0x') {
throw new Error('this tool should not send to a contract');
}
// The exact cost (in gas) to send to an Externally Owned Account (EOA)
var gasLimit = 21000;
@ -73,7 +84,6 @@ Include example links to etherscan showing the transactions
});
});
-----
Transactions Confirm UI (with a Custom Signer)
@ -128,7 +138,17 @@ Transactions Confirm UI (with a Custom Signer)
Coalesce Jaxx Wallets
=====================
Explain how Jaxx uses HD Wallets
The Jaxx Wallet (for iOS, Android, desktop, et cetera) uses HD wallets on Ethereum the
same way as Bitcoin, which results in each transaction being received by a separate
address. As a result, funds get spread across many accounts, making several operations
in Ethereum impossible.
This short recipe will coalesce all these accounts into a single one, by sending the funds
from each account into a single one.
This also results in paying multiple transaction fees (1 fee per account to merge).
@TODO: This is incomplete!!
*Source Code:* ::
@ -139,12 +159,16 @@ Explain how Jaxx uses HD Wallets
var hdnode = ethers.HDNode.fromMnemonic();
hdnode = hdnode.derivePath("m/44'/60'/0'/0");
@TODO:
-----
Access Funds in a Mnemonic Phrase Wallet
========================================
@TODO: This is incomplete
*Source Code:* ::
var ethers = require('ethers');
@ -152,7 +176,7 @@ Access Funds in a Mnemonic Phrase Wallet
var walletPath = {
"standard": "m/44'/60'/0'/0/0",
"electrum": "m/0'" // Non-standard
// @TODO: Include some non-standard wallet paths
};
var mnemonic = "";
@ -163,6 +187,8 @@ Access Funds in a Mnemonic Phrase Wallet
var wallet = new Wallet(node.privateKey);
console.log(wallet.address);
@TODO:
-----
Custom Provider
@ -201,4 +227,4 @@ through to INFURA, but dump all data going back and forth.
-----
\
.. EOF

View File

@ -1,18 +1,14 @@
Getting Started
***************
The Ethers library aims to be:
* Complete
* Compact
* Correct
The ethers.js library is a compact and complete JavaScript library for Ethereum.
-----
Installing in Node.js
=====================
Assuming `npm is installed`_, from the project directory ``ethers.js`` can be added using::
From your project directory::
/Users/ricmoo/my-project> npm install --save ethers
@ -26,16 +22,16 @@ And from the relevant application files::
Including in Web Applications
=============================
For security purposes, it is usually best to place a copy of this script on
For security purposes, it is usually best to place a copy of `this script`_ on
the application's server, but for a quick prototype using the Ethers CDN (content
distribution network) should suffice::
<!-- This exposes the library as a global variable: ethers -->
<script src="https://cdn.ethers.io/scripts/ethers.v2.0.js" type="text/javascript"></script>
<script src="https://cdn.ethers.io/scripts/ethers-v2.0.min.js" type="text/javascript">
</script>
.. _npm is installed: https://nodejs.org/en/
-----
\
.. _npm is installed: https://nodejs.org/en/
.. _this script: https://cdn.ethers.io/scripts/ethers-v2.0.min.js

View File

@ -4,11 +4,11 @@
contain the root `toctree` directive.
What is Ethers.js
What is ethers.js
*****************
This library (which was made for and used by ethers.io) is designed to make it
easier to run client-side JavaScript based wallets, keeping the private key on
easier to write client-side JavaScript based wallets, keeping the private key on
the owners machine at all times.
.. toctree::

View File

@ -1,7 +1,8 @@
Notes
*****
Hello
A few quick notes about some of the less obvious aspects of interacting with
Ethereum in JavaScript.
-----
@ -26,8 +27,8 @@ To demonstrate how this may be an issue in your code, cosider::
false
To remedy this, all numbers (which can be large) in *ethers.js* are stored and manipulated
as Big Numbers (i.e. `BN.js`_).
To remedy this, all numbers (which can be large) are stored and manipulated
as :ref:`Big Numbers <bignumber>`.
The functions :ref:`parseEther( etherString ) <parseEther>` and :ref:`formatEther( wei ) <formatEther>` can be used to convert between
string representations, which are displayed to or entered by the user and Big Number representations
@ -40,7 +41,96 @@ which can have mathematical operations handeled safely.
Promises
========
Hello
A `Promise in JavaScript`_ is an object which simplifies many aspects of dealing with
asynchronous functions.
The most useful operations you will need are:
:sup:`Promise` . all ( promises )
Returns a new promise that resolves once all the *promises* have resolved.
:sup:`prototype` . then ( onResolve, onReject )
Returns another Promise, which once the Promise was resolved, the *onResolve*
function will be executed and if an error occurs, *onReject* will be called.
If *onResolve* returns a Promise, it will be inserted into the chain of the returned
promise. If *onResolve* throws an Error, the returned Promise will reject.
**Examples**
------------
**Cleaning out an account**
::
var targetAddress = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";
var privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123";
var wallet = new ethers.Wallet(privateKey);
// Promises we are interested in
var balancePromise = provider.getBalance(wallet.address);
var gasPricePromise = provider.getGasPrice();
var transactionCountPromise = provider.getCode(wallet.address);
var allPromise = Promsie.all([
gasPricePromise,
balancePromise,
transactionCountPromise
]);
var sendPromise = allPromises.then(function(results) {
// This function is ONLY called once ALL promises are fulfilled
var gasPrice = results[0];
var balance = results[1];
var transactionCount = results[2];
// Sending a transaction to an externally owned account (EOA) is 21000 gas)
var txFeeInWei = gasPrice.mul(21000);
// This will send the maximum amount (our balance minus the fee)
var value = balance.sub(txFeeInWei);
var transaction = {
to: targetAddress,
gasPrice: gasPrice,
nonce: transactionCount,
// The amount to send
value: value,
// Prevent replay attacks across networks
chainId: provider.chainId,
};
var signedTransaction = wallet.sign(transaction);
// By returning a Promise, the sendPromise will resolve once the
// transaction is sent
return provider.sendTransaction(signedTransaction);
});
var minedPromise = sendPromise.then(function(transaction) {
// This will be called once the transaction is sent
// This promise will be resolve once the transaction has been mined.
return provider.waitForTransaction(transaction.hash);
});
minedPromise.then(function(transaction) {
console.log("The transaction was mined: Block " + transaction.blockNumber);
});
// Promises can be re-used for their value; it will not make the external
// call again, and will provide the exact same result every time.
balancePromise.then(function(balance) {
// This *may* return before teh above allPromises, since it only
// required one external call. Keep in mind asynchronous calls can
// be called out of order.
console.log(balance);
});
-----
@ -95,4 +185,6 @@ To convert between ICAP and checksum addresses, see :ref:`getAddress() <api-getA
.. _IBAN: https://en.wikipedia.org/wiki/International_Bank_Account_Number
.. _IEEE 754 double-precision binary floating point: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
.. _BN.js: https://github.com/indutny/bn.js/
.. _Promise in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
.. EOF

View File

@ -5,4 +5,15 @@ Ethers uses a large suite of test cases to help ensure the library is as
complete, backwards compatible and correct as possible and pass
regression as new features are added.
Many of the applications are created procedurally.
Many of the test cases are created procedurally.
**@TODO:**
Explain more here on how to run and add testcases.
**@TODO:**
Post links to the testcases on IPFS (the generated test cases can takes hours to
generate and are too large to check into GitHub)
-----
.. EOF