Key Solidity Commands/Illustrations
Useful foundation solidity commands - Understanding
LLL has been deprecated in late 2019Event : How to generate event when updating a variable
Sample code here under illustrate how to generate an event when we insert an input in the adopters array. You can use https://remix.ethereum.org to see how event are generated. Go on Remix Environment section and select VM (to use the Remix Blockchain Simulator). Select the good compiler version, compile the code, then deploy it with Remix. In the deployed contract section, select the deployed contract and insert an adopter by clicking on "adopt and put 1". Check on the rigth section and click on debut to see the detail. You will see in the logs section the value of the event generated:
[ { "from": "0xd4fc541236927e2eaf8f27606bd7309c1fc2cbee", "topic": "0x9bfa8a75e6c0508ff72126d8b916a6dcc0bfa9920fa37c6ad1b5db12b3218230", "event": "Triggeradoption", "args": { "0": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "1": "1", "_adopter": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "petId": "1", "length": 2 } } ]
Run this sample code to see the event generated on https://remix.ethereum.org
pragma solidity ^0.6.1;
contract Adoption {
address[16] public adopters;
event Triggeradoption(address _adopter, uint petId);
function adopt(uint petId) public returns (uint){
require(petId>0 && petId<15);
adopters[petId]=msg.sender;
emit Triggeradoption(msg.sender, petId);
return petId;
}
function getAdopters() public view returns(address[16] memory){
return adopters;
}
}
Inheritance
contract voter {...}
// Inherite one base contract
contract belgiumVoting is voter { ... }
// Inherite multiple base contract
contract brusselsVoting is voter, belgiumVoting {...}
// Order of inheritence matter in definition
contract diegemVoting is belgiumVoting,voter {...} //!! Doesn't work because voter must be defined first
// Interface Contract definition
interface voting {...}
Library and the Ethereum Package Manager
SmartContract System Design
Proof of existence SC - Sample
// Install Ganache-CLI
$ sudo npm install -g ganache-cli
// Start Ganache-CLI
// Ganache-cli starts a development blockchain that needs to be running
// while develop the application so leave it running while we continue working
// on the application.
$ ganache-cli
// Install Truffle:
$ sudo npm install -g truffle
$ sudo npm install -g @truffle/hdwallet-provider
// Setup Truffle Project
$ mkdir proof-of-existence
$ cd proof-of-existence/
$ truffle init
//In truffle-config.js we need to specify the network that we will be using.
// In the module.exports object, add the following information:
module.exports = {
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*'
}
}
};
//ganache-cli is running our blockchain environemnt on port 8545
//Truffle comes with a Migrations.sol contract that keeps track our migrations
//as well as a 1_initial_migrations.js script to deploy the Migrations.sol contract.
//Run the following command in the terminal in your project directory:
$ truffle migrate
//The terminal should print information about the deployment of the Migrations
//contract.
//Run the following command in the terminal in your project directory:
//Set up the boilerplate code for the contract, a contract definition and
//a contract constructor
$ truffle create contract ProofOfExistence1
//Then copy the content inside of the SC (the functions to be added)
// Create a new migrations file in the migrations directory called 2_deploy_contracts.js.
var ProofOfExistence1 = artifacts.require('./ProofOfExistence1.sol');
module.exports = function(deployer) {
deployer.deploy(ProofOfExistence1);
};
// This script tells Truffle to get the contract information from
// ProofOfExitence.sol and deploy it to the specified network.
// Now we just need to tell Truffle to run the deployment.
$ truffle migrate
// Truffle remembers which contracts it has migrated to the network,
// so if we want to run the migration again on the same network,
// we need to use the --reset option like so:
$ truffle migrate --reset
// Launch Truffle Console to interact with Blockchain
$ truffle console
truffle(development)>
// define a variable that is an instance of teh ProofOfExistence1 deployed:
var poe = await ProofOfExistence1.at(ProofOfExistence1.address)
// find address of the SC deployed
truffle(development)> poe.address
'0x5E7E0167539e117BbD3cE3DAc41b809Cb49fA2Ff'
// Call the notarization function on that SC
truffle(development)> poe.notarize('Hello World!')
{ tx: '0x60ae...2643cbea65',
receipt: …
}
//We can get the proof for the string with
truffle(development)> poe.proofFor('Hello World!')
‘0x7f83b...126d9069’
//And check that the contract’s state was correctly changed
truffle(development)> poe.proof()
'0x7f83b...126d9069'
The hashes match!
// Exit the Truffle console:
truffle(development)> .exit
// create a new file called ProofOfExistence2.sol
$ truffle create contract ProofOfExistence2
// update content of SC with input: https://github.com/ConsenSys-Academy/proof-of-existence-exercise/blob/master/contracts/ProofOfExistence2.sol
// update the 2_deploy_contracts.js to point to the ProofOfExistence2.sol
$ truffle migrate --reset
// Interact with new version (via Javascript)
$ truffle console
// Catch deployed contract in a variable
truffle(development)> var poe = await ProofOfExistence2.at(ProofOfExistence2.address)
// Check if a proof is on registy:
truffle(development)> poe.checkDocument('Hello World!')
false // doc has not been yet notarized
// notarize the document:
truffle(development)> poe.notarize('Hello World!')
// check if notarized:
truffle(development)> poe.checkDocument('Hello World!')
true // doc has been notarized
// Looping over arrays in smart contracts can get expensive as arrays get longer.
// Using a mapping is a better solution.
//Edit truffle-config.js and uncomment the following
// It will import the tool to derive a private key and address from a mnemonic.
const HDWalletProvider = require('@truffle/hdwallet-provider');
// Entering your Infura API key in line 26 will allow you to easily deploy contracts to Ethereum
const infuraKey = "fj4jll3k.....";
// import your seed phrase (from the .secret file) into the file
// Truffle will use these words to access your wallet and deploy the contract.
const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();
// Create Infura ACCOUNT:
// https://blog.infura.io/getting-started-with-infura-28e41844cc89/
curl https://rinkeby.infura.io/v3/76002fe6f0654982aafba3dd73c4f32d -X POST -H “Content-Type: application/json” -d ‘{“jsonrpc”:”2.0",”method”:”eth_getBalance”,”params”: [“0xBf4eD7b27F1d666546E30D74d50d173d20bca754”, “latest”],”id”:1}’
// Update the truffle-config.js
// In order to connect your metamask client to the Infura API gateway to Rinkeby,
// to avoid having to install a local client syncying with Rinkeby network
const infuraURL = 'https://rinkeby.infura.io/v3/<Project_ID>' <-- project id here
// For Truffle to derive our ethereum address from the mnemonic, we need to
// install the Truffle HD wallet provider. In the terminal located in the
// proof-of-existence project root run:
$ npm install @truffle/hdwallet-provider
// import the HD wallet provider into your truffle-config.js file.
var HDWallet = require('@truffle/hdwallet-provider')
// Add the rinkeby network configuration to the networks object in
// truffle-config.js module.exports.
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*'
},
rinkeby: {
provider: () => new HDWalletProvider(mnemonic, infuraURL),
network_id: 4, // Rinkeby's network id
gas: 5500000,
},
}
// run “truffle migrate” for the correct network and your contract will be deployed!
$ truffle migrate --network rinkeby
// ! Error message with Node14:
/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/merkle-patricia-tree/node_modules/async/lib/async.js:358
callback(err);
^
Error: Callback was already called.
// Workaround:
// Truffle is not currently compatible with Node 14. Part of the problem is that Ganache is not yet compatible with Node 14 and Truffle uses that under the hood
$ truffle migrate --network rinkeby --skipDryRun
Multi-Signature Wallet Sample
// Start with the MultiSignatureWallet.sol
// Insert the constructor
constructor(address[] memory _owners, uint _required)
// We also added a mapping of owner addresses to booleans so that we can quickly
// reference (without having to loop over the owners array) whether a specific
// address is an owner or not.
// After having done the code here above, we can interact with it:
// We will use the SimpleStorage.sol contract as target transaction to multisign
// This is the contract that we are going to be calling from the Multisig contract.
// look in the migrations directory you will see the deployment script
// that truffle will use to deploy the SimpleStorage contract as well as the
// MultiSig Wallet
// If you have made update of node, sometimes, it is needed to update also truffle
$ npm install truffle -g
// Then Launch Truffle dev environment
$ truffle develop
// Deploy the 3 contracts (Migration, MultiSig, SimpleStorage)
$ truffle(develop)> migrate
// get the deployed instances of the SimpleStorage.sol and
// MultiSignatureWallet.sol contracts
$ truffle(develop)> var ss = await SimpleStorage.at(SimpleStorage.address)
$ truffle(develop)> var ms = await MultiSignatureWallet.at(MultiSignatureWallet.address)
// Check the state of the SimpleStorage contract
$ truffle(develop)> ss.storedData.call()
// return -->> BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }
// You can verify by waiting for the promise to resolve and converting
//the answer to a string.
$ truffle(develop)> ss.storedData.call().then(res => { console.log( res.toString(10) )} )
// return -->> 0
// If we want to update the SimpleStorage contract data to be 5, the encoded
// function signature and input parameters would look like this:
truffle(develop)> var encoded = '0x60fe47b10000000000000000000000000000000000000000000000000000000000000005'
// So we need to encode the functional call with Param with tool first
// Let's get the available accounts and then make a call to the MultiSig contract
truffle(develop)> var accounts = await web3.eth.getAccounts()
truffle(develop)> ms.submitTransaction(ss.address, 0, encoded, {from: accounts[0]})
// The current state of the MultiSig has one transaction that has not been
// executed and has one confirmation (from the address that submitted it)
// One more confirmation should cause the transaction to execute
$ truffle(develop)> ms.confirmTransaction(0, {from: accounts[1]})
// You should see two log events this time as well. A “Confirmation” event
as well as an “Execution” event. This indicates that the call to SimpleStorage
executed successfully.
// We should see in event on console: ".... event: Execution,".
// We can verify that the state of the contract was updated by running
$ truffle(develop)> ss.storedData.call()
// return --> 5
// check that the address that updated the SimpleStorage contract was the
// MultiSig Wallet.
$ truffle(develop)> ss.caller.call()
‘0x8...xxx’
$ truffle(develop)> ms.address
‘0x8...xxx’
Debugging Truffle Testing
// First clone repo on your PC from Github: https://github.com/ConsenSys-Academy/truffle-test-debugging
// Run following js test:simpleStorageTest.js file
contract('SimpleStorage', async (accounts) => {
it('Calling set(x) should set storedData in storage to x', async() => {
let newValue = 2;
let instance = await SimpleStorage.deployed()
instance.set(newValue, {from: accounts[0]})
let returnedValue = await instance.storedData.call()
assert.equal(newValue, returnedValue, "The returned value should equal the new value.")
})
})
// Launch Blockchain Simulator (use Ganache-CLI here for test)
$ ganache-cli
// Run the test (go in the cloned repo to test this -> truffle-debug/project-dir)
$ truffle test
// you will see that the test report a failure, so let's debut
//If you don see any activity on GANACHE-CLI, it is possible that not closed properly
// from previous test. then ps -al ->> then kill -9 PID
// Check on Ganache-cli interface to see call function and hash. Take the hash and
// use it for the Truffle-debug
$ truffle debug 0xc599abab3c0d47dbfc91c3570dec887928126b36fdf173201abfd9cd33f3694c
// insert "o" to go step by step on the contract exectution and enter "v" within
// each step to see how state variable evoluate.
// here error was that we add +1 to state variable, remote +1 in the SimpleStorage.sol
// then run again test
$ truffle test
Sample Exercice One - Illustration Purpose Only
Sample Exercice 2 - Illustration Purpose only
Ethereum and End-User
Introduction to Web3
The problem with Internet today is your identity and your data that you don't control the access and sharing. The cost of the web is subsidized by the value of your data. This model needs to change. We need an ethical internet to foster innovation and collaboration.
It is time to decentralize the internet (go outside the power of Google, Facebook, Amazon...) and move to version 3:
A web of ethical collaboration, rewarding the contributor
A web where data is not censored
A web where data can be autenticated to their originator
A web where users own their data
Other interesting URL on the topic:
From web 1.0, to 2.0 going to 3.0: https://media.consensys.net/a-warm-welcome-to-web3-89d49e61a7c5 (very good historical artical)
Web3.js javascript API reference documentation: https://web3js.readthedocs.io/en/v1.2.9/ This is the Ethereum Javascrip API. web3.js is a collection of libraries that allow you to interact with a local or remote ethereum node using HTTP, IPC or WebSocket.
Web3.js Javascript Libraries
There are a variety of common JavaScript libraries that you can use to connect to Ethereum.
Truffle: Truffle will connect to a running blockchain specified in the truffle-config.js file, manage deployments via migration scripts and information stored in the truffle artifacts. Truffle provides contract abstractions for interacting with your contracts. Following URL are the basis API explanation, very important: https://www.trufflesuite.com/docs/truffle/reference/contract-abstractions
Web3.js
Web3.js is one of the most popular JavaScript libraries in Ethereum dApp development. There are 2 versions:
web3.js 1.X: https://web3js.readthedocs.io/en/v1.3.0/
web3.js 0.X: https://github.com/ethereum/web3.js/blob/0.20.7/DOCUMENTATION.md (MetaMask is still using that old version !!!)
// Run on Chrome Javascript Console (when MetaMask is well connected to Ganache-GUU
// you can send transactions on the Ganache GUI network through the injected
// web3 object in the browser console javascript
// !!! Metamask support only Web3.js 0.20 today and has injected that version here
// but the exercice here under is done with Web3.js version 1.X
// To do this you need to browse a Web page that has injected web3.js 1.X version
// in my case this is the Consensys page : https://courses.consensys.net/courses/take/blockchain-developer-bootcamp-registration-2020/texts/14509938-8-1-1-web3-js-javascript-library
// Then create a new Web3 instance that will use that web3.js 1.X injected version by
// the consensys page here above
> web3 = new Web3(web3.currentProvider)
// Return you current Metamask account address (connected to Ganache)
> web3.currentProvider.selectedAddress
// It returns your Account address
// Send a transaction via js console of Chrome:
// Use the following code snippet as your transaction information.
// Work only for web3.js v1.0 beta (not for other version, please check the API)
var transaction = {
from: web3.currentProvider.selectedAddress,
to: "0xf875cE190d4B1f8f9507b28c12a850DD9f3150e7",
value: web3.utils.toWei("1", "ether")
}
// Now send the transaction to ledger:
web3.eth.sendTransaction(transaction)
// if you get an error you may need to reset the web3 provider:
web3.setProvider(web3.currentProvider)
// Metamask will pop-up for signature, if you hav a JSON-RPC error, this is because
// you are using Metamask on different ledger and the nonce value could be the one
// buffered from the previous Metamask connection to the other ledger.
// So you simply need to reset the nonce value in Metamask
// If this happens, it is a simple fix. Open Metamask and click the account icon
// on the upper right and select "Settings". In the "Advanced" area,
// select "Reset Account".
// check balance of account (valid with Web3.JS version 1.x)
> await web3.eth.getBalance("0x42e071ADfF5eeAA8Aa1B28daEd347d71fF6C59CA")
// or also
> await web3.eth.getBalance(web3.currentProvider.selectedAddress)
// Issue with the MetaMask version that is injecting web3 0.20 that doesn t
// Support those calls. You need a call back function to make it work:
web3.eth.getBalance('0x42e071ADfF5eeAA8Aa1B28daEd347d71fF6C59CA',function(error,result){
if(error){
console.log(error)
}
else{
console.log(result)
}
})
// Previous function is working without metamask injected web3 lib v0.20!!!
You can look at how to connect to contract with web3.js with https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#web3-eth-contract
Ether.js
Ether.js library is on https://docs.ethers.io/v5/
// Connect to Web3 Provider: (in the Chrome Javascript console, while beeing
// on the Consensys page here above that inject ether.js lib
/ MetaMask injects a Web3 Provider as "web3.currentProvider", so
// we can wrap it up in the ethers.js Web3Provider, which wraps a
// Web3 Provider and exposes the ethers.js Provider API.
> const provider = new ethers.providers.Web3Provider(web3.currentProvider);
// There is only ever up to one account in MetaMask exposed
const signer = provider.getSigner();
// Create the transactin:
var transaction = {
to: "0xf875cE190d4B1f8f9507b28c12a850DD9f3150e7",
value: ethers.utils.parseEther("1")
}
//S end it, enter
> signer.sendTransaction(transaction)
Connecting web3.js to contract
// Type to following commands in your javascript console in Chrome
// Initialize the web3.js lib in your javascript browser console to use it
> web3 = new Web3(web3.currentProvider)
// To use the SimpleStorage Contract deployed on Rinkeby, you need to initialise
// an object in javascript consol, using web3 lib
// We provide to our web3 object with the SC API and address
> const SSaddress = "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6"
// Set the ABI in the javascript console
> const ABI = [
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
}
],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "newValue",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "updatedBy",
"type": "address"
}
],
"name": "storageUpdate",
"type": "event"
},
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
// Then create the Rinkeby SimpleStorage Running SC, instance on Javascript
// console, via web3 lib
> const simpleStorage = new web3.eth.Contract(ABI, SSaddress)
// if you don't have any more Ether on Rinkeby, use faucet: https://www.rinkeby.io/#faucet
// Read the Contract State, so the value of storeData state variable
> simpleStorage.methods.get().call().then(console.log)
// Insert a value in the Storage SmartContrat
> simpleStorage.methods.set(9).send({from: web3.givenProvider.selectedAddress})
// Running this code should trigger Metamask to ask you to sign a transaction
// This open the Wallet and you just need to validate the transaction
// Validate the transaction writing
> simpleStorage.methods.get().call().then(console.log)
// Subscribe to events with simpleStorage
> simpleStorage.events.storageUpdate(function(error, event){console.log(event)})
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#contract-events
// To trigger this event, you will have to call the "set()" function on the contract again
{address: "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6", blockHash: "0x06611e65952cab4abe3d4b5379b6fa3470c0def67dc75899c6c100a686e3baf1", blockNumber: 7415667, logIndex: 23, removed: false, …}
address: "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6"
blockHash: "0x06611e65952cab4abe3d4b5379b6fa3470c0def67dc75899c6c100a686e3baf1"
blockNumber: 7415667
event: "storageUpdate"
id: "log_cb9c26a9"
logIndex: 23
raw: {data: "0x000000000000000000000000000000000000000000000000…0000000007c584e710fe6afa5369a8bffee47fc98d76535a5", topics: Array(1)}
removed: false
returnValues: p {0: "9", 1: "0x7C584e710fE6afA5369A8Bffee47Fc98D76535a5", newValue: "9", updatedBy: "0x7C584e710fE6afA5369A8Bffee47Fc98D76535a5"}
signature: "0x8920a650e770f0ad1e6f22b8db2e2aebfad8ce417eddbb244a5499495a727c15"
transactionHash: "0x31a59ff98a318ebe471c23803a30606975ee82c8ff6671fc3ced9ff9c953859f"
transactionIndex: 20
__proto__: Object
Building Truffle for the Web
As reference for inspiration: https://www.trufflesuite.com/tutorials/pet-shop#creating-a-ui-to-interact-with-our-smart-contract
We are going to build a Browser App interface to interact with SC.
// GO to your petshop directory
$ truffle compile
// It creates artifact in build/constrats section
// Create the 2_deploy.contracts.js (to deploy the SC on ledger)
// Edit truffle-config.js to point to port 8545 of your local ganache-cli
// Launch Ganache-cli on your machine
$ ganache-cli
// Then deploy the SC on ganache-cli ledger (run this command in your truffle project directory)
$ truffle migrate
// You should see succesfull deployed
// Go on you Visual Studo Code editor on folder /src/js/app.js (we are going to build the web interface)
// Connect your Metamask client to the local Ganache-CLI (on port 8545)
// Select localhost:8545, import account 1 of ganache by taking private key
// Then click on account in metamask to select connect. You should the Ether value of the account on the account on Metamask
// If not, client connect account on metamask
// Then launch the Web server hosting the app.js
$ npm run dev
// This will launch the lite server
// Then go to your Chrome Browser and run localhost:3000 to see the app and interact
Update to Metamask
Metamask automatically injected the web3 object into the browser window object, making it available for any application to use it. Metamask now provides the option to no longer automatically inject the web3 object, something called "privacy mode". Which requires that dapps ask permission to view users’ accounts. Dapps should update their code to support this feature.
dapps must now request access to user accounts by calling a new method on the provider: ethereum.enable(). This method returns a Promise that’s either resolved with user accounts after user approval, or rejected with an Error after user rejection.
Integrating with React
The following sample exercice is usefull to understand how to integrate with React framework: https://www.trufflesuite.com/boxes/react
Other usefull pages:
// Use Truffle React box
$ mkdir truffle_react
$ cd truffle_react
// Install the React truffle box
$ truffle unbox react
// Go to truffle-config.js and add the local ganache Ledger
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
// Go to Truffle_reac/client/src/getWeb3.js
// add the local httpProvider 127.0.0.0.1:8545
// port 8545 for ganache-cli and 7545 for ganache-ui
// Launch ganache-cli
$ ganache-cli
// Copy mnemonic or private key and import on Metamask in Chrome browser
// Migrate contract to ganache
$ truffle migrate // from inside the truffle_react directory
// Start the React web app
$ cd /client
$npm run start
// This will start the Web client app (app.js)
// you will have a pop-up to validate transaction by Metamask
// change the value in the simple storage via the app.js file
Rimble UI
Rimble is a React component library that makes creating Web3 applications in react much easier. The library is under active development by the ConsenSys Design team.
Other usefull URL on the subject:
https://rimble.consensys.design/components/rimble-ui/ToastMessage (to notify user that a transaction is done)
// Install Rimble UI
$ npm install --save rimble-ui styled-components
// To include a Button, this is all you need:
import React, { Component } from 'react'
import { Button } from 'rimble-ui'
class Example extends Component {
render () {
return (
<Button size={'medium'}>
Click me!
</Button>
)
}
}
Smart Contracts Pitfalls, Testing, and Debugging
Covered on this section:
Writing tests for Solidity smart contracts in Javascript and Solidity using the Truffle framework
Smart contract best practices
Smart contract exploits and dangers
Optimizing Gas
Go over a safety checklist of things to consider before deploying your contracts
Writing Test
Test Driven Development: Developers write tests first, then implement functions to make the tests pass. We write test for:
Verify correct Behaviour
Consider Edge Cases
Consider range of possible user input
No necessarily for finding bugs only:
It is about contract behaviour
TDD is about behaviour defined first through unit test
Test in Truffle can be writen in Solidity or Javascript
Testing contract should not extend any existing contract
With Truffle, we import:
Assert.sol library (from Truffle, planned for test)
DeployedAddresses.sol (from Truffle, planned for test): The addresses of your deployed contracts (i.e., contracts that were deployed as part of your migrations) are available through the
truffle/DeployedAddresses.sol
library.+ the contract we want to test
Remark regarding the test contract:
Contract name should start with T
Function test must beging with t (test...)
// We import the DeployedAddresses.sol truffle made SC for accessing the targeted SC
// to bet tested
import "truffle/DeployedAddresses.sol";
contract TestAdoption {
// The address of the adoption contract to be tested
Adoption adoption = Adoption(DeployedAddresses.Adoption());
// We import the Assert.sol SC from Truffle to compare value
// We all the time compare the value returned by the SC function (returnedId)
// to an expected value expectedPetId
Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
// use memory keyword when you need just tempor variable in SC for check
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
Kooks:
There exists also provided hooks like in Mocha Javascript Testing Framework
Use to setup and teardown before and after each test
It is possible for the setup function to exceed the gaz limit, then play with beforeEachAgain() hook to solve that problem
Test with Javascript: (see example pet-shop-tutorial/test/testAdoption.test.js)
Truffle use the Mocha Framework and chai for assertion
use contract() instead of describe()
before each contract(), the contract are redeply, meaning state variable rest also (clean state)
you can still use describe() when contract() is not necessary
// To import SC in javascript test file, use require
const Adoption = artifacts.require("Adoption");
// contract() provides a list of account
// web3 is provided in the testing environment (to be put there)
// A web3 instance is available in each test file, configured to the correct provider. So calling web3.eth.getBalance just works!
// 2 ways to call test: to be called from withing the project truffle repo
// Exectue the SC.test + the javascript test
$ fruffle test
// Exectue only the javascrip test
$ cd test/
$ truffle test testAdoption.test.js
Other good references for testing writing:
Writing Solidity test with Truffle: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-solidity
Writing Javascript test with Truffle: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
Throws test: https://www.trufflesuite.com/tutorials/testing-for-throws-in-solidity-tests : Truffle 3 brings forth Solidity unit testing, which means one can now test contracts in Solidity itself. This is a boon to contract developers, as there are several reasons why it's useful to have Solidity tests in addition to Truffle’s Javascript tests.
Catch Error in Javascript test: https://ethereum.stackexchange.com/questions/48627/how-to-catch-revert-error-in-truffle-test-javascript
Catching Javascript error
Try catch
// When a VM Exception is encountered by Truffle, it throws an Error.
// We can catch this error in a try...catch block.
async function tryCatch(promise) {
try {
await promise;
throw null;
}
catch (error) {
assert(error, "Expected a VM exception but did not get one");
assert(error.message.search(errorString) >= 0, "Expected an error starting with '" + errorString + "' but got '" + error.message + "' instead");
}
};//
This tryCatch function will first execute the try block that will wait for the promise that it received to resolve. If it resolves without throwing an error, then the last line of the try block will throw an empty error. So no matter what, whether the promise succeeds or fails, the catch block will be executed, receiving whatever error information was thrown in the try block.
Special error handling
We can make this error handling function even more specific so that it can tell us why a specific error is being thrown. The VM exception messages provide details as to why an exception was thrown. For example, whether the exception was due to a revert, if the transaction ran out of gas, if there was an invalid JUMP or another invalid OPCODE, if there was a stack overflow or underflow or if there was a static state change. We can add these details to our errorString to catch specific cases.
// save this file called something like "exceptionHelpers.js" in
// your Truffle test directory
const errorString = "VM exception while processing transaction: ";
async function tryCatch(promise, reason) {
try {
await promise;
throw null;
}
catch (error) {
assert(error, "Expected a VM exception but did not get one");
assert(error.message.search(errorString + reason) >= 0, "Expected an error starting with '" + errorString + reason + "' but got '" + error.message + "' instead");
}
};
module.exports = {
catchRevert : async function(promise) {await tryCatch(promise, "revert" );},
catchOutOfGas : async function(promise) {await tryCatch(promise, "out of gas" );},
catchInvalidJump : async function(promise) {await tryCatch(promise, "invalid JUMP" );},
catchInvalidOpcode : async function(promise) {await tryCatch(promise, "invalid opcode" );},
catchStackOverflow : async function(promise) {await tryCatch(promise, "stack overflow" );},
catchStackUnderflow : async function(promise) {await tryCatch(promise, "stack underflow" );},
catchStaticStateChange : async function(promise) {await tryCatch(promise, "static state change");},
};
In the catch block, the first assertion statement checks whether the error message contains any information. If the promise successfully executed, this first assertion statement will fail and will let you know that it "Expected a VM exception but did not get one." So "error!=0" -> it executes the texte after. If the first assertion statement passes, and the error message does contain information, then the second assertion statement will be executed. The statement will search the error message for a predefined string.
How to include in our .js test?
Now to include this in your Javascript tests, you can save this file called something like "exceptionHelpers.js" in your Truffle test directory. You can import specific functions and include them in your tests! This test will pass as long as the buyTickets() function throws a revert!
// Add this in you testing.js function
var EventTickets = artifacts.require('EventTickets')
let catchRevert = require("./exceptionsHelpers.js").catchRevert
contract('EventTicket', function(accounts) {
// ...
it("tickets should only be able to be purchased when the msg.value
is greater than or equal to the ticket cost", async() => {
const instance = await EventTickets.new(description, url, ticketNumber)
await catchRevert(instance.buyTickets(1, {from: secondAccount, value: web3.utils.toWei("0.5", "ether")}))
})
// ...
}
Other References on the subject:
Open Zeppelin Test helper: https://github.com/OpenZeppelin/openzeppelin-test-helpers
Smart Contract Best Practices
Different than other programing language:
Cost of failure can be high
Change can be difficult
Move Slow and be carefull
Prepare for failure:
Code will have bug
Setup a circuit breaker design pattern
Manage amount of money at risk with rate limiting or caps
Setup upgrade path for bug fixes and improvement
Roll-out very carefully
Catch Bug before release
Automated testing
Use Testnets
Time locking Alpha and beta release
3th party security audit
But bounties (Prime)
Keep contract simple
Complexity => more bugs
Moduralize the code, with simple block
Use Audited and battle tested code when possible
Prefer clarity than performance
Only use blockchain when necessary
Unique Environment
Know the env
Be very carefull when calling external contract
Function are public
malicious actors will try calling your function in a way you didn't intend or foresee
contract data is public
Limits proper to this sytem (contrains)
gaz price
block gaz limit
Fundamental trade-offs
trade-offs between engineering and security
Ideal system is modular, reuse code, and support upgradable component
Rigidity versus upgradability
on SW dev perspective, upgradability support is good but on SC it highers the maleability, complexity and surface attack
For simple function, simplicity is focus before upgradability support
Monolithic versus Modular
A single Monolithic SC keep all data local, identifiable and controlable, can make code review easier
Modular can be easier to maintain on SW point of view
Code duplication versus reuse
Use proved, deployed, audited code when possible
Other usefull reference:
Exploits and Dangers
There is a SmarContract Weakness Classification Registry (SWC) under: https://swcregistry.io
The Smart Contract Weakness Classification Registry (SWC Registry) is an implementation of the weakness classification scheme proposed in EIP-1470. It is loosely aligned to the terminologies and structure used in the Common Weakness Enumeration (CWE) while overlaying a wide range of weakness variants that are specific to smart contracts.
The goals of this project are as follows:
Provide a straightforward way to classify security issues in smart contract systems.
Define a common language for describing security issues in smart contract systems' architecture, design, or code.
Serve as a way to train and increase performance for smart contract security analysis tools.
Re-entracy Attack (SWC 107) https://swcregistry.io/docs/SWC-107
Problematic because calling external contracts passes control flow to them. The called contract may take over the control flow and end up calling the smart contract function again in a recursive manner (you could have then an un-ending loop or abuse loop). If you can’t remove the external call, the next simplest way to prevent this attack is to do the internal work before making the external function call.
// Insecure Target SC that call function execution from the calling SC
//Example of a user calling this function that will implement in his calling
// SC amountToWrithdraw() that call transfer on an INSECURE way
mapping (address => uint) private userBalances;
function transfer(address to, uint amount) {
if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call transfer()
userBalances[msg.sender] = 0;
}
// In this case, the attacker can call transfer() when their code is executed on the external call in withdrawBalance. Since their balance has not yet been set to 0, they are able to transfer the tokens even though they already received the withdrawal. This vulnerability was also used in the DAO attack.
// Ask yourself, if multiple contract functions are called, what happens?
How to mitigate:
Good idea to handle your internal contract state changes before calling external contracts
A more complex solution could implement mutual exclusion, or a mutex. This allows you to lock a state and only allow changes by the owner of the lock.
Additionnal complementary info on: https://consensys.github.io/smart-contract-best-practices/known_attacks/#race-conditions42
Transaction Ordering and Timestamp Dependence (SWC-114) https://swcregistry.io/docs/SWC-114
Here we focus on how transactions are included in the blockchain and considerations around the process.
Transactions that are broadcast to the network but have not yet been included in a block are in the mempool. Miners choose the order in which to include transactions from the mempool into a block that they are mining. Also, since transactions are in the mempool before they make it into a block, anyone can know what transactions are about to occur on the network. This can be problematic for things like decentralized markets. Protecting against this is difficult and you will likely need to devise contract specific solutions. Decentralized markets can mitigate concerns by implementing batch auctions (enchère) or using a pre-commit scheme, where the details are submitted after the transaction is committed.
Integer Overflow and Underflow (SWC-101) https://swcregistry.io/docs/SWC-101
Integers can underflow or overflow in the EVM (SWC-101). This happens when an arithmetic value oversteps the minimum or maximum size of a type. Reference
An overflow/underflow happens when an arithmetic operation reaches the maximum or minimum size of a type. For instance if a number is stored in the uint8 type, it means that the number is stored in a 8 bits unsigned number ranging from 0 to 2^8-1. In computer programming, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of bits – either larger than the maximum or lower than the minimum representable value.
Underflow is a similar situation, but when a uint goes below its minimum value it will be set to its maximum value.
Denial of Service with Failed Call (SWC-113) https://swcregistry.io/docs/SWC-113
External calls can fail accidentally or deliberately, which can cause a DoS condition in the contract. To minimize the damage caused by such failures, it is better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically (this also reduces the chance of problems with the gas limit).
Remediation: it is recommended to follow call best practices:
Avoid combining multiple calls in a single transaction, especially when calls are executed as part of a loop
Always assume that external calls can fail
Implement the contract logic to handle failed calls
Denial of Service by Block Gas Limit or startGas (SWC-128) https://swcregistry.io/docs/SWC-128
When smart contracts are deployed or functions inside them are called, the execution of these actions always requires a certain amount of gas, based of how much computation is needed to complete them. The Ethereum network specifies a block gas limit and the sum of all transactions included in a block can not exceed the threshold.
Programming patterns that are harmless in centralized applications can lead to Denial of Service conditions in smart contracts when the cost of executing a function exceeds the block gas limit. Modifying an array of unknown size, that increases in size over time, can lead to such a Denial of Service condition.
There is a limit to how much computation can be included in a single Ethereum block, currently 10,000,000 gas worth
How transaction are generally ordered in block (Miner can decide), but this is a trend: https://ethereum.stackexchange.com/questions/6107/what-is-the-default-ordering-of-transactions-during-mining/6111#6111
Remediation:
Caution is advised when you expect to have large arrays that grow over time. Actions that require looping across the entire data structure should be avoided.
If you absolutely must loop over an array of unknown size, then you should plan for it to potentially take multiple blocks, and therefore require multiple transactions.
Force Sending Ether
Another danger is using logic that depends on the contract balance. Be aware that it is possible to send ether to a contract without triggering its fallback function. Using the selfdestruct
function on another contract and using the target contract as the recipient will force the destroyed contract’s funds to be sent to the target.
It is also possible to pre-compute a contracts address and send ether to the address before the contract is deployed.
The contract’s balance will be greater than 0 when it is finally deployed.
Complementary interesting links:
A survey of Attack on Ethereum SC/ https://eprint.iacr.org/2016/1007.pdf
List of known bugs: https://solidity.readthedocs.io/en/develop/bugs.html
SC Weakness: https://swcregistry.io
Testing hack under game: https://solidity-05.ethernaut.openzeppelin.com
Game to hack Eth SC to learn security: https://capturetheether.com
Optimizing Gaz
Reducing the gas consumed by a contract is important in two situations:
Cost of deploying a contract
Cost to call the contract functions
The Solidity optimizer tries to improve the efficiency of your contract as much as possible during compile time
Gaz cost per opcode: https://docs.google.com/spreadsheets/d/1n6mRqkBz3iWcOlRem_mO09GtSKEKrAsfO7Frgx18pNU/edit#gid=0
Short Circuit Rules
The operators ||
and &&
apply the common short-circuiting rules. This means that in the expression f(x) || g(y)
, if f(x)
evaluates to true, g(y)
will not be evaluated even if it may have side-effects.
// How can these functions in Unoptimized.sol be modified to reduce gas usage?
// https://gist.github.com/ConsenSys-Academy/a61670fd8796d73d8b4b7d5935f9e714
// Not optimal way
function shortCircuit() public view returns(bool){
if (oftenFalse || oftenTrue) {
return true;
}
}
// Better way
function shortCircuit2() public view returns(bool){
if(oftenTrue && oftenFalse) {
return false;
} else {
return true;
}
}
Expensive operations in a loop
Modifying storage variables in a loop can be very expensive and should be avoided unless absolutely necessary. How can this function be improved, given that loop is a storage variable? Here is the source file.
function looping (uint x) public returns (bool) {
for(uint i; i < x; i++){
loops += 1;
}
return true;
}
Reduce the number of loops
Zero loops is ideal, but sometimes you just have to loop. Since loops are expensive, can you reduce the number of loops in your functions?
function looping2 (uint x) public pure returns(bool){
uint m = 0;
uint v = 0;
for(uint i = 0; i < x; i++){
m += i;
}
for(uint j = 0; j < x; j++){
v -= j;
}
return true;
}
Fixed size byte arrays
The type byte[]
is an array of bytes, but due to padding rules, it wastes 31 bytes of space for each element (except in storage). It is better to use the bytes
type instead. see https://solidity.readthedocs.io/en/latest/types.html#fixed-size-byte-arrays
Other good document references:
Optimizing your solidity contract gas usage: https://medium.com/coinmonks/optimizing-your-solidity-contracts-gas-usage-9d65334db6c7
Paper on under-optimize SC devouring your money https://arxiv.org/pdf/1703.03994.pdf
How to write SC that optimize Gas: https://medium.com/better-programming/how-to-write-smart-contracts-that-optimize-gas-spent-on-ethereum-30b5e9c5db85
Safety Checklist
SWC Registry
For a comprehensive, maintained list of smart contract weaknesses, along with test cases and examples of vulnerable smart contracts, check out the SWC Registry. This is a community maintained project and includes most, if not all of the well known vulnerabilities and hacks, with contract samples and suggestions for how to address weaknesses.
Additional Resources:
Logic Bugs :
run unit test to avoid logic bug
following standard coding and best practices
avoid complex rule and implementation
Recursive call
Can be dangerous, re-entrancy attack with external SC call (this was exploited in the DAO attack)
Integer arithmetic overflow
integers will automatically and silently wrap if too small or too large
max for uint = 2^256-1 (which is a quite huge number)
Usint uint8 -> can be dangerous if user can insert data without control of his value
Poison Data
Like a user changing a state variable so that it oveflows.
Assume user will input data we don't expect
validate and sanatize user input data
What contract expose to the world
Be aware
! Soliditiy functions default to public
Any code that go to production should be audited
All code and data (state variable) are public on blockchain (all storage variable are public)
Making them private only make them not accessible to SC directly
!!! Private storage variable can still be observed by external blockchain user
Miner Vulnerabilities
Miners can manipulate block timestamp and which transaction can be in a block, during a limited windows time frame. Sometimes better to include a timestamp inside the SC state variable
Malicious Admin
Dangerous to allocat to specific address power on SC. Better to work with multisig contract.
Off-chain safety
Use tradditional Web security best practices:
HTTPS
2 factors authentication
encryption
Cross-chain Replay attack
When hardforks are happening on a blockchain, any transaction on one is valid on the other one. So one transaction posted on one blockchain could be run on the other one, without our consent, because valid by default
When hardforks are happening on a blockchain, any transaction on one is valid on the other one. So one transaction posted on one blockchain could be run on the other one, without our consent, because valid by default
TX Origin and Gas limit
!!! Use msg.sender
in place of tx.origin
Looping over a undetermined range of a array could be dangerous. We could exeed block gas max limit
Run Test for gas usage in our SC tests
Limit the lenght of user supplied data
Security Analysis Tools
A quick and easy way to check your Solidity code for common bugs is to use security analysis tools. Static analysis tools will parse your code and highlight potential security vulnerabilities.
Code online check tool: https://tool.smartdec.net
The more popular security tool: https://mythx.io
MythX is the premier security analysis API for Ethereum smart contracts. It detects many common Solidity and EVM bytecode vulnerabilities. This makes it attractive for developers and auditors alike. It can be used right away with integrations for Truffle, Remix, Brownie, or from the system CLI. There are also libraries (https://docs.mythx.io/en/latest/building-security-tools/index.html#language-bindings) available as a starting point for building custom security tools such as IDE extensions and CI scripts.
MythX is a security analysis API that allows anyone to create purpose-built security tools for smart contract developers. Tools built on MythX integrate seamlessly into the development environments and continuous integration pipelines used throughout the Ethereum ecosystem.
Exercice to do:
Create an account on https://mythx.io/
First install the MythX security plugin for Truffle Framework: https://github.com/ConsenSys/truffle-security
// Go to https://github.com/ConsenSys/truffle-security
// Install the NPM Pluggin (Mythx)
// ! It is going to be deprecated
// They recommend to use another interface https://github.com/dmuhs/mythx-cli
$ npm install -g truffle-security
Generate the Mythx API key: Generate your API key in the tools section of the MythX dashboard.
The key can be passed to Truffle either via the MYTHX_API_KEY
environment variable or the --apiKey
command line argument. For security reasons it is recommended to always pass the token through an environment variable, e.g. defined in the settings of a Continuous Integration (CI) server or a shell script that can be sourced from.
Set the following enviromment variables to your API key (add this to your .bashrc
or .bash_profile
for added convenience):
$ export MYTHX_API_KEY='Put your API key here!'
// Or more simple on CLI the security check to see if Mythx discover security issue
$ truffle run verify --apiKey ddddd
// You can see the report on MythX interface: https://dashboard.mythx.io/#/console/analyses/e82cb004-5918-4b4b-a3a5-57f7a35bb635
To test Mythx, we will use the following Github project: https://github.com/ConsenSys-Academy/simple-coin In the Visual Studio, go to source control on the left, '...' source, clone, github "consensys-Academy/simple-coin
MythX can also be used on Remix!
Additional Resources
MythX: https://mythx.io
Mythril: https://github.com/ConsenSys/mythril
MythX Truffle Pluggin: https://github.com/ConsenSys/truffle-security
tx.origin Attack Demo
Ref Material:
tx.origin
is a global variable in Solidity which returns the address of the account that sent the transaction. Using the variable for authorization could make a contract vulnerable if an authorized account calls into a malicious contract. A call could be made to the vulnerable contract that passes the authorization check since tx.origin
returns the original sender of the transaction which in this case is the authorized account.
The global variable tx.origin
in Solidity always references the address of the original sender of the transaction, of the full call chain. See the reference in the docs here. This is different than message.sender
in that message.sender
references the address of the sender of the current call.
You should never use tx.origin
in Solidity for authorization
Additional References:
Denial of service attack example
Shows how a contract may be susceptible to a denial of service attack by an unexpected revert (SWC-113)
External calls can fail accidentally or deliberately, which can cause a DoS condition in the contract. To minimize the damage caused by such failures, it is better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically (this also reduces the chance of problems with the gas limit).
Re-entrancy attack example
A reentrancy attack -> (https://swcregistry.io/docs/SWC-107) l
Integer over/under flow example
Shows what an integer overflow vulnerability (https://swcregistry.io/docs/SWC-101) looks like. This type of attack is easily avoidable by using a SafeMath library such as this, that provides safety checks and will revert on error.
MythX
MythX is a smart contract security service for Ethereum.
Advanced Ethereum Topics
We will cover the following points:
Common Smart contract design patterns
Designing upgradable smart contract systems
What are Oracles, and how do you use one?
Using the Ethereum Name Service
An introduction to IPFS
Formal Verification and smart contracts
SmartContract Design Pattern
We will go over some of the most common design patterns that developers use when writing smart contracts.
Good Principle: Fail early and fail loud
Restricting Access
You cannot prevent people or computer programs from reading your contracts’ state. The state is publicly available information for anyone with access to the blockchain. However, you can restrict other contracts’ access to the state by making state variables private.
contract C1 {
uint private internalNum;
}
You can restrict function access so that only specific addresses are permitted to execute functions. This is useful for allowing only designated users, or other contracts to access administrative methods, such as changing ownership of a contract, implementing an upgrade or stopping the contract.
contract Admin {
mapping(address => bool) admins;
modifier onlyAdmin {
require(admins[msg.sender] == true);
_;
}
function addAdmin(address _a)
public
onlyAdmin
returns(bool)
{
admins[_a] = true;
return true;
}
}
More on access control with Zepplin: https://docs.openzeppelin.com/contracts/2.x/access-control
Auto-Deprecation
The auto deprecation design pattern is a useful strategy for closing contracts that should expire after a certain amount of time. This can be useful when running alpha or beta testing for your smart contracts.
!!! Remember that using timestamps such as the now keyword are subject to manipulation by the block miners in a 30-second window.
pragma solidity ^0.4.25;
contract Autodeprecate {
uint contract_expired;
modifier is_active {
if(!expired()) _;
}
modifier when_deprecated {
if(expired()) _;
}
constructor(uint duration) {
contract_expired = now + duration;
}
function expired()
view
public
returns(bool)
{
return now > contract_expired ? true : false;
}
}
Mortal
Function to kill a SC. Implementing the mortal design pattern means including the ability to destroy the contract and remove it from the blockchain. You can destroy a contract using the selfdestruct
keyword. The function to do it is often called kill. It takes one parameter which is the address that will receive all of the funds that the contract currently holds. As an irreversible action, restricting access to this function is important.
As compl info sample: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/56de324afea13c4649b00ca8c3a3e3535d532bd4/contracts/access/Ownable.sol
pragma solidity ^0.5.0;
import "./Ownable.sol";
contract Mortal is Ownable {
function kill()
{
if(msg.sender == owner()) selfdestruct(address(uint160(owner()))); // cast owner to address payable
}
}
Withdrawal pattern (pull over push payment)
See also: https://gist.github.com/critesjosh/80d41928db2310684bc7660aa45873da, there is a separation of function logic. The split()
function handles the accounting and divides the msg.value
sent with the transaction. Another function, withdraw()
, allows accounts to transfer their balance from the contract to their account.
This pattern is also called the withdrawal pattern. It protects against re-entrancy and denial of service attacks that we will cover in the next lesson.
Circuit Breaker
Circuit Breakers are design patterns that allow contract functionality to be stopped. This would be desirable in situations where there is a live contract where a bug has been detected. Freezing the contract would be beneficial for reducing harm before a fix can be implemented.
contract CircuitBreaker {
bool public stopped = false;
modifier stopInEmergency { require(!stopped); _; }
modifier onlyInEmergency { require(stopped); _; }
function deposit() stopInEmergency public { … }
function withdraw() onlyInEmergency public { … }
}
In a situation such as this, you would also want to restrict access to the accounts that can modify the stopped state variable, maybe to the contract owner (such as multisig wallet) or a set of admins.
State Machine
Contracts often act as a state machine, where the contract has certain states in which it behaves differently and different functions can and should be called. A function call often ends a stage and moves the contract to the next stage (especially if the contract models interaction). It is also common that some stages are automatically reached at a certain point in time.
Speed Bump
Speed bumps slow down actions so that if malicious actions occur, there is time to recover.
pragma solidity ^0.4.25;
contract SpeedBump {
uint allowedTime;
modifier allowed_every(uint _time){
if(now > allowedTime){
_;
allowedTime = now + _time;
}
}
constructor(uint duration){
allowedTime = 0;
}
function setAllowedTime(uint _time)
public
returns(bool)
{
allowedTime = now + _time;
return true;
}
}
Used to limit issue of DAO: https://github.com/blockchainsllc/DAO
The Ethereum Name System
Resolve Human readable name to machine readable identifiers:
Name to Ethereum Address
Name to IPFS Hash
Name to Swarm Hash
It aims at solving the same problem as DNS for Internet (Name -> IP Address)
ENS offer Ethereum address resolution but the system could be extensed, enabling further resources to be resolved in the future.
ENS Architecture:
Registry SmartContract:
Single Contract
List domain, subdomain and data
The Data include the owner of the domain that can be an external account or contract
Store the address of the Resolver SC for the domain and the Time to live
Domain Owner can
set the resolver they want to use
can transfer the ownership of the domain
can change also ownership of subdomain
Resolver SmartContract:
Translate name to machine readable identifier
Any contract that implement the standard resolver can be setup as resolver
Each record type (Ethereum, ipfs) defines a method the resolver must implement
New Record type can be submitted via EIP (Ethereum improvement proposal) to community
Additional Resources:
IPFS
IPFS stand for the Inter-planetary file system
Hashes in IPFS start with "Qm..."
The js-ipfs library allows you to use IPFS without having to run a local IPFS node
It aims to replace HTTP by addressing file content (with hashes) rather than file locations
It uses p2p protocols, making it a decentralized network
It complements blockchain nicely, as it allows immutable, off-chain data storage
Other benefits include bandwidth saving for servers, potential content availability improvements, censorship and deletion resistance and data deduplication
IPFS is a distributed Peer to Peer file system:
no servers
only node
nodes store files and cryptographic hashes of files
Files are adressed by hashes
A single network sharing git objects
Good complement to Ethereum for decentralized applications:
Maintain Data Integrity of the resources that it references (change it content data -> change in hash so change in address)
Peer to peer and decentralized, so no SPOC and resilient
Cheaper Data Storage than blockchain
Store Data on IPFS
Store IPFS hash on SC
Additional Resources:
https://ipfs.io
Upgradable contract
Bugs are inevitable -> upgradability strategy needed for SC
Modular design -> Enable to upgrade some component and not all
Plan for upgrade contract is important, but lots of research on it
2 approaches for managing upgradable SC:
Maintain a registry SC storing the address of the latest version of SC
Or use a contract (Proxy) that forward Data and call to the correct SC
Upgrade take time:
Incorporate security measure, like circuit breaker
Registry SC:
Store the latest contract address -> this requires user make a query for every time they want to use a SC to know the latest address
Disadvantage: if user not use it, it is pushing data on old SC version + how to manage data migration
Forward Data and Call SC:
Avoid problem of registry contract
User only interact with User relay contract
Attention on storage point:
Must be the same between upgrade
Additional Resources:
https://eips.ethereum.org/EIPS/eip-2535 -> The diamond standard a new way to upgrade SC
Oracle with Rhombus
Oracle provides off-chain data into SC
Oracle are like an API for Blockchain
You could build your own oracle, but some cons:
How can a user be sure he can trust your oracle?
Some 3th party Oracle Service provider:
Oraclize (a leading one for Ethereum, bitcoin...)
Smartcontract.com
Oracle logic is "it this, then I give you that info"
Recursive queries are possible
An Oracle queries require:
Data Source Type
URL
Wolfram Alpha (https://www.wolframalpha.com) (Retrieve an answer to question )
IPFS (retrive a file from an IPFS location)
Random byte
Computation (provide the result of arbitrary computation)
Query (to the Data Source type)
Authenticity Proof type (optional)
This proof enables to reduce the trust user must have in Oracle
From withing a SC, we can make queries to Oracle SC, passing data source and query to it. The Oracle SC then answer to the _fallback() function of the calling SC that can catch in variable the answer and perform update of its state variable. In the calling SC definition, we use contract Co2Level is usingOraclize {...}
We can add proof of authenticity check
Additional Resources:
Provable Things Documentation -> Very usefull to read!
Rhombus network
In the context of blockchain, oracles are an industry term for an information source that delivers real world data to smart contracts on a blockchain. Oracle providers must ensure truthfulness of the data provided:
First off, the real world data must be obtained from a trusted place. There is no blockchain system to guarantee data accuracy, so this data is often obtained from a trusted and verified source.
Second, the oracle should also provide proof that the data has not been tampered with, either by external parties, or the oracle service provider itself.
We will sample how this works thanks to a step by step sample code on how to incorporate real world data into your smart contracts with the Rhombus Lighthouse Delivery method -> https://github.com/RhombusNetwork/tutorial
Rhombus provides oracle data through a mechanism called a lighthouse. In simple terms, a lighthouse is an address where a single integer value that contains off-chain data is stored. Rhombus ensures the value is updated periodically, and can provide guarantees that the value is correct. -> https://rhombus.network
In this case, there is a random number generator lighthouse already deployed on Rinkeby, and you must access this lighthouse from your casino project smart contract (which will also be deployed on Rinkeby)
// include the Lighthouse Interface file ILighthouse.sol in the smart contract
// file where you want to read data into, Gamble.sol.
import "../contracts/Ilighthouse.sol";
// Ilighthouse file contains the interface for a Lighthouse Object.
// Now you can declare a global ILighthouse object in your Gamble.sol file
ILighthouse public myLighthouse;
// Look at the https://github.com/RhombusNetwork/tutorial, tuto is well explained
Formal Verification
The goal is to make writing secure smart contracts as easy and accessible as possible. One route to this goal is via formal verification. From Wikipedia, formal verification is the act of proving or disproving the correctness of intended algorithms underlying a system with respect to a certain formal specification or property, using formal methods of mathematics. Said another way, it is a way to prove that a program is correct for all inputs. This can ensure that a hacker cannot modify the contract to an unintended state.
Formal verification is a method to prove a program correct for all inputs
MythX is also able to formally verify smart contracts. This guide gives more details on how to formally verify a smart contract in Remix using the MythX analysis API.
In Remix, go to the section plugin and search for Mythx, add it. Then in settings, add API and login /Password of your Mythx Account. Then you can run basic test.
Other usefull links:
Zero Knowledge Proof
First production on ZKP is from the 1980, some initial publications can be found on https://amturing.acm.org/award_winners/goldwasser_8627889.cfm
The goal of zero-knowledge proofs (ZKPs) is for a verifier to be able to convince themselves that a prover possesses knowledge of a secret parameter, called a witness, satisfying some relation, without revealing the witness to the verifier or anyone else.
Zero-knowledge proofs are currently being researched as solutions to two main issues in public blockchains:
Privacy: All essential information within a blockchain network must be public. ZKPs allow users to verify / prove certain elements of information (such as the answer to a crossword puzzle) while not revealing the information itself (to not give the answer to someone else). Certain tools, such as AZTEC, promise to use ZKPs to provide channels which shield the entire nature of certain transactions from the public blockchain while still using the public blockchain's security promises
Scalability: As blockchain networks grow, the amount of data they require to be maintained grows as well. ZKPs compress large amounts of data into compact, verifiable proofs, a characteristic scalability researchers hope to use to dramatically reduce the data required to maintain security on a public blockchain.From ZCash:“Succinct” zero-knowledge proofs (SNARKs, STARKs) can be verified within a few milliseconds, with a proof length of only a few hundred bytes even for statements about programs that are very large.
A common way Zero-Knowledge Proofs are used in blockchain is as follows. A Verifier will take the piece of data to be verified and put it through a private process (ZK-SNARK or STARK, for example) which produces a program called a Proof Circuit. This Proof Circuit can then be posted openly (such as in a Smart Contract) as it reveals no meaningful information about the nature of the data it represents. A Prover can then use the Proof Circuit to irrefutably prove they know the piece of data by publicly "solving" the Proof Circuit, a process that also reveals nothing about the nature of the data.
zk-SNARKs
Succinct Non-Interactive ARgument of Knowledge, or SNARKs, were first widely implemented on a blockchain by ZCash, a fork of Bitcoin. Ethereum introduced SNARK capacity with the Byzantium fork by including precompiled contracts that allowed efficient SNARK verification on-chain. SNARKs are being discussed in terms of privacy (as in ZCash) and scalability (as in SNARK roll-ups)
Other links:
zk-STARKs
Scalable and Transparent ARgument of Knowledge, or STARKs, share a similar fundamental ideas with SNARKs but differ in execution. Their development and advocacy is done chiefly by STARKware, a private company led by Eli Ben Sasson, with assistance from the Ethereum Foundation.
No trust setup and faster than SNARK
Other links:
AZTEC
Anonymous Zero-Knowledge Transactions with Efficient Communication, or AZTEC, is a way of executing confidential transactions on a public blockchain like Ethereum. The AZTEC Protocol is a series of op-codes (based on range proofs) embedded in smart contract libraries deployed by the AZTEC Protocol team (a private company acquired by ConsenSys) on Ethereum. Using a combination of these op-codes along with an off-chain Javascript library, the AZTEC Protocol team has developed a design pattern they call the "AZTEC Note," which allows for individuals to generate and transfer their own version of these notes on-chain without revealing internal transactions to external parties.
Other references:
ZKP Tutorials
"Hello World" of zk-SNARKS with Zokrates (ZoKrates is a toolbox for zkSNARKs on Ethereum. It helps you use verifiable computation in your DApp, from the specification of your program in a high level language to generating proofs of computation to verifying those proofs in Solidity.)
More Resources
Introduction to zk-SNARKS with Examples (article)
Not-so-gentle Introduction to the PCP Theorem (PCP Theorem underpins many ZKPs) (article)
The Math behind STARKs (abstract but still technical) (5-part article series)
Etherum 2.0
Launch of Ethereum in 2015, First roadmpa https://blog.ethereum.org/2015/12/24/understanding-serenity-part-i-abstraction/
If we were to think of the Ethereum network as a house, launch of Ethereum mainnet was the building of the house and everyone moving in. Network forks are the patches, repairs and additional features to address challenges that arise from more people moving into the house. The upgrade to Ethereum 2.0 ("Serenity" on the roadmap) will be akin to building an entirely new foundation and house to accommodate the next generation of network growth.
Imagine the Ethereum 2.0 house being beside the Ethereum 1.x home. At first, there will be a path connecting the two. Ultimately, Ethereum 2.0 will expand to include Ethereum 1.x within it.
Ethereum 2.0 has three phases:
Phase 0 - Beacon Chain (Proof-of-Stake)
Phases 0 is the implementation of a Proof-of-Stake consensus mechanism for Ethereum. The blockchain secured by Phase 0's PoS mechanism is called the Beacon Chain. Miners for the Beacon Chain are called validators.
Phase 0 is the phase most public one now, as it's currently undergoing public testing and there are multiple companies developing software for it. Here is a list of the main validator clients:
Teku — Java-based client developed by ConsenSys Software
Prysm — Go-based client developed by Prysmatic Labs
Lighthouse — Rust-based client developed by Sigma Prime
Lodestar — Javascript-based client
Nimbus — Nim-based client developed by Status
The Medalla testnet was the first, public, multi-client testnet available. You can check out its progress here and join the testnet by following these steps.
Phase 1 - Shards (Data Availability and Coordination)
Database sharding is used in conventional computer programming to increases scalability of large systems. From this article. Ethereum 2.0 leverages traditional database sharding to decrease the amount of memory needed to maintain the full state of the network. Originally meant to be 1024 shards, the current spec will produce 64 database shards. Each of these shards will have their own validators. They will periodically check into the beacon chain using crosslinks, which is a summary of the state of that shard and the only representation of the shard on the Beacon Chain.
Phase 2 - Execution Environments (Computation)
The final phase of Ethereum 2.0 deals with the execution environments present on each shard. in Ethereum 1.x, the execution environment is the Ethereum Virtual Machine, a Turing-complete programming language that provides a universal computation environment for all in the network to use.
However, this universality comes at an efficiency cost. The EVM is slow compared to modern processing languages. Phase 2 addresses this processing cost by using a version of WebAssembly, a new type of code developed by Mozilla. It allows code written in C, C++ or Rust but executed in the browser to run at near-native speeds. (For more information about the implications of WebAssembly for the broader web, please see this video: "Rust, WebAssembly and the Future of Serverless")
In Phase 2, each shard will be allowed a unique execution environment. While at least one will be running the EVM (for sake of continuity), it's possible others will be running execution environments (EEs) for Libra, Bitcoin, or any other blockchain network.
Research is still developing in this area and the specs have not been frozen. If you'd like to try an experimental execution environment prototyping tool for Ethereum 2.0, please see Scout.
The Beacon Chain (that connect the shard chains):
In August 2020, the Ethereum 2.0 developers launched the Medalla testnet. It is the first multi-client public testnet for Ethereum 2.0 Beacon Chain. It's exciting because you can get involved to run a Beacon Chain client and stake test-Ether to mimic the actions of a validator. All of this is to test the clients and find major issues before the launch of the actual Beacon Chain.
Near the end of the course, we'll be doing a walkthrough of setting up and staking on the Medalla testnet using multiple clients.
Relevant URL:
Ethereum Yellow paper: https://ethereum.github.io/yellowpaper/paper.pdf
Eth2.0 Spec: https://github.com/ethereum/eth2.0-specs
To follow progress on Eth2: https://hackmd.io/@benjaminion/eth2_news/https%3A%2F%2Fhackmd.io%2F%40benjaminion%2Fwnie2_200822
Additional Topics
LLL (Low-level Lisp-like Language)
LLL has been deprecated in late 2019. Lisp Like Language (LLL) is a low level language similar to Assembly. It is meant to be very simple and minimalistic; essentially just a tiny wrapper over coding in EVM directly. LLL has direct access to storage and memory of the EVM so you can determine exactly where your data sits. When you use an EVM opcode in LLL, it translates directly to the bytecode representation of that opcode. In fact, all EVM opcodes are available to LLL.
Generally LLL binaries are about 30 to 40% smaller than the equivalent in Solidity. LLL also uses prefix notation, where operators are placed to the left of their operands.
Example implemenation of SC in LLL: https://github.com/benjaminion/LLL_erc20#erc20lll-an-implementation-of-ethereum-erc20-tokens-in-lll
The registry contract of the Ethereum Name service (ENS) was written in LLL mainly because of the gas savings due to its size and the brevity of its code: https://github.com/ensdomains/ens
Introduction to Vyper
Vyper is an experimental, contract-oriented, pythonic programming language that targets the Ethereum Virtual Machine. Beware, Vyper is not ready for production use cases.Vyper is not trying to be a replacement for Solidity. It is meant to be a more security focused smart contract programming language and will likely not be able to do everything that Solidity can.
Other references:
Writing SC in Vyper
Sample banking code in VIper: https://github.com/ConsenSys-Academy/simple-bank-vyper
You need to install the Vyper compiler yourself for Truffle to be able to compile Vyper contracts.
Installing Vyper on Linux is as simple as
sudo snap install vyper --edge --devmode
Otherwise, consult the docs.
Ethereum Improvement proposal (EIP)
Ethereum is an open source project with no one company or organization that controls the direction of the project. There is an open governance model where everyone is free to propose and discuss changes to the system. The EIP system is the forum to do this. You can view the EIP website here.
The explanation of the EIP Process: https://eips.ethereum.org/EIPS/eip-1
There are several different stages that an EIP can be in. Draft EIPs are works in progress, are open for consideration and discussed on GitHub. Accepted EIPs can be expected to be included in the next hard fork upgrade. You can see a list of the accepted EIPs for the Byzantium upgrade in EIP 609. Final EIPs are proposals that have already been adopted and deferred EIPs are not being considered for immediate adoption, but may be considered again in the future.
An EIP includes the following sections:
Preamble (metadata)
Simple summary
Abstract - A short description of the issue
Motivation - Why is the existing protocol inadequate
Specification - Describes the syntax and semantics of the new feature
Rationale - Why did you make these design decisions?
Backwards compatibility - Explain any backward incompatibilities and how they will be addressed
Test Cases - These are mandatory for EIPs proposing consensus changes
Implementation - What does an implementation of the EIP look like? This section must be completed before the EIP is given the status “Final”.
ERC= Ethereum request for Comment
The development of tokens on Ethereum is under active development. Explore these EIPs to follow the development of token standards.
Additional Resources:
Additional Tips and Tricks
A transaction hash on Ethereum is calculated as followed:
txhash = keccak256(signedTransaction)
So when you call a smartcontract method, you can not within that method access the transaction hash, because you need first to sign that transaction, this is the chicken and eggs problem. You can via a second transaction after access that info.
Last updated
Was this helpful?