"We are not what we know but what we are ready to learn" || Industrial Engineer turned data analyst turning Blockchain Developer
27,293 words
https://github.com/r1oga @r1oga

To decentralize or not decentralize?

I discovered cryptocurrencies in 2017. First it was not even Bitcoin, but Bitcoin Cash. Then Ethereum and its merry flock of tokens. Then I caught the virus. The virus spread. I eventually went beyond cryptocurrencies and got interested by the technology itself: Blockchain. Cryptocurrencies remain my favorite use case. Therefore I deliberately expose myself to a significant amount of information and news related to cryptocurrencies. Decentralization has always been one of the most (if not THE) discussed subjects within the community as it is also the most dividing one. Indeed decentralization is such a good example of a classical Manichean narrative.

Narrative

Like any good Manichean story, it boils down to a fight of Good against Evil. Vilains threat to destroy the Good. While most are sinking into despair, some Heroes discover some secret weapons and decide to put up a fight against the Vilains. Let's have a look at a couple of techologies used as Weapon by Heroes to fight a War against Vilainst. Will the Heroes emerge triumphing? That is not the point of this post. The point is: these ingredients make a damn good plot, don't they! As proof that the plot is good: there are already movies or documentaries on all them...

Heroes Vilains Weapons Wars Movies
Timothy C.May, Eric Hughes, Julien Assange, Edward Snowden Governement, State, Corporations, Big Brother Cryptography: link1, link2 privacy & anonymity vs surveillance, free speech vs censorship Fifth Estate, Snowden
Richard Stallman, Linus Torvalds Software & OS sellers GNU, Linux sharing vs selling, "friendship vs "obey the law", freedom vs being at the mercy of, destruction vs creation Revolution OS
Satoshi Nakamoto Banks Bitcoin hard money vs soft money, deflationary vs inflationary, P2P vs intermediated, borderless vs national, financial sovereignity vs financial slacevery and coercion, centralized vs decentralized Banking on Bitcoin, Cryptopia, The Rise and Rise of Bitcoin
...

Software developers fight evil software sellers shooting at them Open Source Licenses.
Jounalists and activists fight censorship from a repressive State using encrypted connections.
Citzens fight against crooked bankers for their financial sovereignity firing Bitcoins transactions.
In the context of the 2008 financial crisis, multiples privacy leaks scandals, massive inflation in Venezuela, people being dismissed access to basic financial services... how can one not being triggered when being presented these technologies in such a "Darth-Vader-vs The-Rebellion" way?
I was personnally totally triggered.

Today

Decentralization, or the fact of being controlled by a random crowd instead of by one lucky individual or group, fits perfectly in this narrative which confronts bad guys and good guys.
Unfortunately, this narrative has lead to the development of a decentralization dogma: decentralized is good/better/safe, centralization is bad/worse/unsafe.
Dogmas make people rely on beliefs and opinions instead of critical analysis to find what is actually better. Because of this particular decentralization dogma, some blockchain projects make the mistake of focusing on being decentralized before actually building something useful. Conversely, useful but centralized projects have sometimes been illegitimately dismissed.

The mistake: decentralization is the weapon, not the war.

The mistake (that I have made) is that decentralization is a means to an end. It shall not be the goal. Decentralization is a way (Weapon) for a product or system to get some properties and eventually become resistant to some threats (Villains & War).

Product/System Threats Required properties to resist the threats <-- Can be enabled by
Software Software not improving, not being able to customize or fix, not being able to teach, high license costs, privacy issues.Losing users. Reliability, secure, open source code decentralization
Website Downtime, being hacked, censorship, data loss, no privacy, surveillance... Losing users Reliability, secure, censorship resistance, distributed, data redundancy decentralization
Currency, Money system Debasement, theft, unfair taxes, limited to 1 country, system shutdown or collapse, not available 24/7, high fees, no privacy, surveillance Reliability, secure, resistance to censorship, to Denial of Service attacks, borderless, open, sovereignity, P2P, tamper-evident ... decentralization
Media, Press, Information Censorship, being used as propaganda, misinformation Reliability, censoship resistance, independance, self-hosted, distributed, P2P, tamper-evident... decentralization
Oracles Wrong price feed, wrong infos being communicated. Leading to $xxx costs Reliability decentralization

In the end, it is all about threat analysis, risk management and making tradeoffs between different possible mitigation solutions.
"Is it decentralized?" is the wrong question.

Question is not Question should be
Is the software open source? What are the costs of not being able to share the code? What are the costs of the license? What are the costs of the software being malicious and not being able to check it myself?
Is this currency decentralized? How likely may the currency be debased unexpectedly? How likely may someone take control of my funds?
Is this Dapp decentralized? How likely may the app be down? What are the costs of the app execution being manipulated?
Is this oracle decentralized? What are the costs of being lied to? Of getting a wrong price feed? What are the risks of believing a manipulated oracle?
Is this governance model decentralized? Will the governance be more efficient if it is decentralized?
Is it decentralized Will decentralization increase my returns?
Is it decentralized? Will I be able address bugs and attacks quicker by becoming more decentralized?

Bitcoin: A Peer-to-Peer Electronic Cash System was created in 2008 to become an alternative to the current traditional payment system. Previous attempts like DigiCash failed especially because it was centralized and it lead to bankrupcy.
So a first threat is that the network may die due to an unsustainable security/operations model. Another serious threat that Bitcoin faces is the threat of simply being shutdown.

These threats motivated the choice of decentralized system. Indeed decentralization significantly reduces or suppresses them.

The result of a thoughful threat analysis and answering the right questions tell which solution is best: the best solution is the one which decreases the most the total costs associated with identified risks.
That solution won't be black or white but will be the optimum on a decentralization "spectrum".

Mea Culpa & Conclusion

People should care first about whether something works under the threat model they care about.
Decentralization should not be the primary goal. Innovation, building better products and a better digital infrastructure should be. Decentralization is a means among others towards these goals.
It is about building new things worth decentralizing, or make existing things better by decentralizing them.
It is not about making a "decentralized-version" of an existing product that is overall worse, or building decentralized products not worth using in the first place.
Personally, the "threat model I care about" still makes me lean towards decentralized solutions in several situations. But now I can rationalize why.

The networked species

Sapiens can cooperate in extremely flexible ways with countless numbers of strangers. That’s why Sapiens rule the world, whereas ants eat our leftovers and chimps are locked up in zoos and research laboratories." - Y.N.Harari, Sapiens: A brief History of Humankind.

Humans rule the world because they can collaborate flexibly at scale. One way they found to cooperate is forming networks:

A network of people or institutions is a large number of them that have a connection with each other and work together as a system. Collins dictionary

Initially isolated individuals that connect and cooperate with their neighbours becomes a group. The group becomes a tribe. The tribe a society. The society a civilisation.
The strength of the network comes from its participant pooling their ressources and following the same rules to allocate ressources effectively.

However, two fundamental facts prevent networks from either being built in the first place or from operating effectively:

  1. Different people follow different ends
  2. Ressources are scarce

Types of networks

"There are essentially only three ways that I can get another person to help me achieve my end's: love, trade, and force" - D.Friedman, The Machinery of Freedom.

How are the network rules enforced?

"Love" network: I want what you want

Love means situations where help is voluntarily given out of love/kindness: I wish to see someone get what they want. So their goal is mine. Or their goal and mine are aligned/close. Cooperation happens naturally.
This requires the participants to either:

  1. love each other
  2. know each other, know what the other wants, wanting the same thing

So such networks are unfortunately reduced to small groups such as a family. Indeed the higher the number of people, the more unlikely to love them all. It is also difficult to know well enough a big number of people to either love them or know that we want the same thing.

"Mob" network: do what I want or you'll get into trouble

This is the Law of the Strongest. People are hurt or threatened into doing things (providing their ressources). These "things" being what the strongest wants. This strongest person can be a King, a Dictator, a Totalitarian State. Or an elite...
Indeed, less obviously, religion for instance can also be seen as a "Mob" network. Although not physically threatened, a central authority (Pope, clergymen) gets to fix their dogmatic rules and controls information (Bible). Religion coerces society into following their ways and believing their says.
The most libertarian people will even say that any State is coercive. Because the State ultimately relies on Police and military force or threat of emprisonment to enforce Law. Law that citizens don't follow voluntarily.

"Mob" networks are superior to "love" networks in several ways:

  1. they are efficient at large scale
  2. they don't require people to know each other
  3. they don't require people to want the same things. They are not asked what they want. Only the "Mob" gets to decide.

Violence, coercion or guns made possible to build efficient Empires, and are still making possible to build Dictatorships or States.

From a moral point of view, one should wish to an alternative though.

"Money" networks: pay me and I'll do what you want

This is trade. Help me, give me something, or pay me, and I'll do what you want.

The trade has to be voluntary. Otherwise it becomes economic coercion and a form of "mob" network again.
Trade and economic incentives are very effective at a big scale. Our society as we know it, with its technological progress and its cities, has been built thanks to trade. People buying, selling, exchanging goods and services voluntarily because it was in their economic interest.

This kind of network (or capitalism) is not perfect and is legitimately being criticized nowadays. Without a proper Legal and Justice framework it can dangerously lead to a society unfairer than it was. However it is superior to the two previous networks:

  1. efficient at large scale
  2. doesn't require people to know each other
  3. not coercive

Let's get political

Depending on the "incentive" that leads to cooperation: love, coercion or money, we seem to recognize different political systems.

Incentive Network Political System
Love Love network communism/socialism
Coercion Mob network Dictatorship
Trade Money market Capitalim

Is it so simple? Especially, are communism and socialism "love networks"? They wish they were...
Out these 3 types of networks, only networks that rely on trade or coercion are efficient at large scale. In our connected and digital world, we precisely need networks that are efficient at a large scale.
This is why communism and socialism can't work at the scale of a nation. Can millions of citizens want the same thing? Despite sharing and equality being some of their core values, communism and socialism make the tragic and naive mistake to believe it can scale. It unfortunately can't. History confirmed it multiple times. They pretend to be "love" networks but turn out to be "mob" networks. They assume that people who have more will voluntarily give to strangers who have less. If this assumption were true, why would we need socialism in the first place? What about charity? The reality is that people don't voluntarily share (or too little and not often enough) with strangers. So socialism is actually about coercion. A central state that relies on e.g taxes to achieve their end. They take money that is not theirs to reallocate it as centrally planned.

Conclusion

Just like different people follow different ends, different people have different hierarchy of values. I value freedom more than equality. Especially I am reluctant to pay the coercion cost of equality.
Depending on the scale at which it shall operate, can't we consider a particular networks/political system to be best?

Scale Political system Attitude
Small (family) Communism "What belongs to me belongs to the family"
Groups that include people I know well enough or love (friends, community) Socialism "I'll voluntarily help you out and give you what you need."
Anything else Trade/Capitalism "Let's make a deal"

Ethernaut: Levels 19 to 21

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Alien Codex
Shop

Level 19 - Denial

Target

This is a simple wallet that drips funds over time. You can withdraw the funds slowly by becoming a withdrawing partner.
If you can deny the owner from withdrawing funds when they call withdraw() (whilst the contract still has funds) you will win this level.

Contract

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Denial {

    using SafeMath for uint256;
    address public partner; // withdrawal partner - pay the gas, split the withdraw
    address payable public constant owner = address(0xA9E);
    uint timeLastWithdrawn;
    mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances

    function setWithdrawPartner(address _partner) public {
        partner = _partner;
    }

    // withdraw 1% to recipient and 1% to owner
    function withdraw() public {
        uint amountToSend = address(this).balance.div(100);
        // perform a call without checking return
        // The recipient can revert, the owner will still get their share
        partner.call.value(amountToSend)("");
        owner.transfer(amountToSend);
        // keep track of last withdrawal time
        timeLastWithdrawn = now;
        withdrawPartnerBalances[partner] = withdrawPartnerBalances[partner].add(amountToSend);
    }

    // allow deposit of funds
    function() external payable {}

    // convenience function
    function contractBalance() public view returns (uint) {
        return address(this).balance;
    }
}

Weakness

The withdraw function uses call to send ETH to an unknown address. This poses two threats:

  1. Reentrancy (see Level 10 - Reentrancy: the recipient can implement a malicious fallback that will call back ('reenter') the withdraw function
  2. Out Of Gas (OOG) error: call forwards all gas. The recipient may consume it all to prevent the execution of the following instructions. ## Solidity Concepts: error handling
expression syntax effect OPCODE
throw if (condition) { throw; } reverts all state changes and deplete gas version<0.4.1: INVALID OPCODE - 0xfe, after: REVERT- 0xfd deprecated in version 0.4.13 and removed in version 0.5.0
assert assert(condition); reverts all state changes and depletes all gas INVALID OPCODE - 0xfe
revert if (condition) { revert(value) } reverts all state changes, allows returning a value, refunds remaining gas to caller REVERT - 0xfd
require require(condition, "comment") reverts all state changes, allows returning a value, refunds remaining gas to calle REVERT - 0xfd

So the main difference is that assert depletes all gas while revert and require don't. require is a less verbose version of revert.
When to use which error handling method? According to the solidity documentation

The assert function should only be used to test for internal errors, and to check invariants. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix.
The require function should be used to ensure valid conditions that cannot be detected until execution time. This includes conditions on inputs or return values from calls to external contracts.

Hack

We want to make the owner.transfer(amountToSend); instruction fail right after the partner.call.value(amountToSend)(""); instruction. As call forwards all gas, we will cause an Out Of Gas error.

  1. Deploy a malicious contract and set it as withdraw partner with setWithdrawPartner
  2. Cause an Out Of Gas Error by implementing a malicious fallback (that receive the ETH sent by the partner.call.value(amountToSend)("") instruction)
    • Option 1: reenter in denial.withdraw()
    • Option 2: assert a false condition

Takeaways

See Level 10 - Reentrancy takeaways.

Level 20 - Alien Codex

Target: claim ownership of the contract.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/ownership/Ownable.sol';

contract AlienCodex is Ownable {

  bool public contact;
  bytes32[] public codex;

  modifier contacted() {
    assert(contact);
    _;
  }

  function make_contact() public {
    contact = true;
  }

  function record(bytes32 _content) contacted public {
    codex.push(_content);
  }

  function retract() contacted public {
    codex.length--;
  }

  function revise(uint i, bytes32 _content) contacted public {
    codex[i] = _content;
  }
}

Weakness

codex is stored as a dynamic array. retract() reduces codex length without checking against underflow. So it is actually possible to set the codex array length to 2²⁵⁶ -1, which gives power to modify all storage slots.

Solidity Concepts: storage layout of dynamically sized variables

Each smart contract running on the Ethereum Virtual Machine maintains its own state using a key:value storage mapping. The number possible of keys is so huge that most keys actually contain empty values. Each key is called a slot. They are 2²⁵⁶ - 1 slots. Each slot can contain 32 bytes of data.
In the Level 8 -Vault, I listed the basic storage layout rules. Each statically sized variable gets a reserved slot which is defined at compilation time. But what about dynamically sized variables? As their size is not fixed beforehand, how to know which slots to reserve?
With regular hard drive space or RAM an allocation step to find free space to use exists, which is followed by a release step to put that space back into the pool of available storage. The number of storage locations of a smart contract is so huge that it manages its storage differently. It just needs to figure a way to define a storage location to start from. Indeed the likelihood of having location clashes is (not rigorously) 0.

Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
For dynamic arrays, [the] slot stores the number of elements in the array (byte arrays and strings are an exception, see below). For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution). Array data is located at keccak256(p) and the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation.

storage of dynamic array
storage of mapping

Hack

  1. Analyse storage layout

    Slot # Variable
    0 contact bool(1 bytes] & owner address (20 bytes), both fit on one slot
    1 codex.length
    keccak256(1) codex[0]
    keccak256(1) + 1 codex[1]
    2²⁵⁶ - 1 codex[2²⁵⁶ - 1 - uint(keccak256(1))]
    0 codex[2²⁵⁶ - 1 - uint(keccak256(1)) + 1] --> can write slot 0!
  2. call make_contact to be able to pass the contacted modifer

  3. call retract: this provokes and underflow which leads to code.length = 2^256 - 1

  4. Compute codex index corresponding to slot 0: 2²⁵⁶ - 1 - uint(keccak256(1)) + 1 = 2²⁵⁶ - uint(keccak256(1))

  5. Call reverse passing it index and your address left padded with 0 to total 32 bytes as content

Takeaways

Modifying a dynamic array length without checking for over/underflow is very dangerous as it can expand the array's bounds to the entire storage area of 2256 - 1. This can possibly enable modifying the whole contract storage.

Level 21 - Shop

Target: get the item from the shop for less than the price asked.

Contract

pragma solidity ^0.5.0;

interface Buyer {
  function price() external view returns (uint);
}

contract Shop {
  uint public price = 100;
  bool public isSold;

  function buy() public {
    Buyer _buyer = Buyer(msg.sender);

    if (_buyer.price.gas(3000)() >= price && !isSold) {
      isSold = true;
      price = _buyer.price.gas(3000)();
    }
  }
}

Weakness

Like for the Level 11 - Elevator, Shop never implements the price() function from the Buyer interface. An attacker can create a contract that implements its own version of this function.

Solidity Concepts

  • Interfaces
  • Inheritance
  • External function call with gas() option

    When calling functions of other contracts, you can specify the amount of Wei or gas sent with the call with the special options .value() and .gas(), respectively.

  • Gas cost to modify storage
    Ethereum Yellow Paper screenshot - Fee Schedule

    Hack

    buy() is calling price() twice:

  • In the conditional check: the price returned must be higher than 100 to pass

  • To update the price: here is the opportunity to return a value lower than 100.

So we need to implement a malicious price function that:

  • returns a value higher than 100 on its first call
  • returns a value lower than 100 on its second call
  • costs less than 3000 gas to execute. So we can't write in storate. We will read isSold instead to perform a conditinal check: isSold() ? 1: 101

Security Takeaways

  • Don't let interface function unimplemented.
  • It is unsafe to approve some action by double calling even the same view function.

Solutions on GitHub

Ethernaut: Levels 16 to 18

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Recovery
MagicNumber

Level 16 - Preservation

Target

A contract creator has built a very simple token factory contract. Anyone can create new tokens with ease. After deploying the first token contract, the creator sent 0.5 ether to obtain more tokens. They have since lost the contract address.
This level will be completed if you can recover (or remove) the 0.5 ether from the lost contract address.

Contract

pragma solidity ^0.5.0;

contract Preservation {

  // public library contracts 
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 
  uint storedTime;
  // Sets the function signature for delegatecall
  bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));

  constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
    timeZone1Library = _timeZone1LibraryAddress; 
    timeZone2Library = _timeZone2LibraryAddress; 
    owner = msg.sender;
  }

  // set the time for timezone 1
  function setFirstTime(uint _timeStamp) public {
    timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }

  // set the time for timezone 2
  function setSecondTime(uint _timeStamp) public {
    timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }
}

// Simple library contract to set the time
contract LibraryContract {

  // stores a timestamp 
  uint storedTime;  

  function setTime(uint _time) public {
    storedTime = _time;
  }
}

Weakness

  1. Preservation uses Libraries: Libraries use delegatecalls. [Level 6 -Delegation] taught us that using delegatecall is risky as it allows the called contract to modifiy the storage of the calling contract.
  2. Storage layouts of Preservation and LibraryContract don't match: Calling the library won't modifiy the expected storedTime variable. ## Solidity Concept: libraries > Libraries are similar to contracts, but their purpose is that they are deployed only once at a specific address and their code is reused using the DELEGATECALL (CALLCODE until Homestead) feature of the EVM. This means that if library functions are called, their code is executed in the context of the calling contract, i.e. this points to the calling contract, and especially the storage from the calling contract can be accessed.

So Libraries are a particular case where functions are on purpose called with delegatecall because preserving context is desired.

Hack

As libraries use delegatecall, they can modify the storage of Preservation.
LibraryContract can modify the first slot (index 0) of Preservation, which is address public timeZone1Library. So we can "set" timeZone1Library by calling setFirstTime(_timeStamp). The uint _timeStamp passed will converted to an address type though. It means we can cause setFirstTime() to execute a delegatecall from a library address different from the one defined at initialization. We need to define this malicious library so that its setTime function modifies the slot where owner is stored: slot of index 2.

preservation hack workflow

Takeaways

Level 17 - Recovery

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Recovery {

  //generate tokens
  function generateToken(string memory _name, uint256 _initialSupply) public {
    new SimpleToken(_name, msg.sender, _initialSupply);

  }
}

contract SimpleToken {

  using SafeMath for uint256;
  // public variables
  string public name;
  mapping (address => uint) public balances;

  // constructor
  constructor(string memory _name, address _creator, uint256 _initialSupply) public {
    name = _name;
    balances[_creator] = _initialSupply;
  }

  // collect ether in return for tokens
  function() external payable {
    balances[msg.sender] = msg.value.mul(10);
  }

  // allow transfers of tokens
  function transfer(address _to, uint _amount) public { 
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
  }

  // clean up after ourselves
  function destroy(address payable _to) public {
    selfdestruct(_to);
  }
}

Weakness

The generation of contract addresses are pre-deterministic and can be guessed in advance.

Solidity Concepts: selfdestruct, encodeFunctionCall, & generation of contract addresses

  • selfdestruct: see [Level 7 - Force] Sefdestruct is a method tha can be used to send ETH to a recipient upon destruction of a contract.
  • encodeFunctionCall At Level 6 - Delegation, we learnt how to make function call even though we don't know the ABI: by sending a raw transaction to a contract and passing the function signature into the data argument. More convenienttly, this can be done with the encodeFunctionCall function of web3.js: web3.eth.abi.encodeFunctionCall(jsonInterface, parameters)
  • generation of contract addresses, from the Etherem yellow paper, section 7 - contract creation:

Ethereum Yellow Paper screenshot - contract address generation

So in JavaScript, using the web3.js and rlp libraries, one can compute the contract address generated upon creation as follows.

// Rightmost 160 digits means rightmost 160 / 4 = 40 hexadecimals characters
contractAddress = '0x' + web3.utils.sha3(RLP.encode([creatorAddress, nonce])).slice(-40))

Hack

  1. Instantiate level. This will create 2 contracts:
    • nonce 0: Recovery contract
    • nonce 1: SimpleToken contract
  2. Compute the address of the SimpleToken:
    • sender = instance address
    • nonce = 1
  3. Use encodeFunctionCall to call the destruct function of SimpleToken instance at address. ## Takeaways > Contract addresses are deterministic and are calculated by keccack256(rlp([address, nonce])) where the address is the address of the contract (or ethereum address that created the transaction) and nonce is the number of contracts the spawning contract has created (or the transaction nonce, for regular transactions). Because of this, one can send ether to a pre-determined address (which has no private key) and later create a contract at that address which recovers the ether. This is a non-intuitive and somewhat secretive way to (dangerously) store ether without holding a private key. An interesting blog post by Martin Swende details potential use cases of this.

Level 18 - MagicNumber

Target

provide the Ethernaut with a "Solver", a contract that responds to "whatIsTheMeaningOfLife()" with the right number.

Contract

pragma solidity ^0.5.0;

contract MagicNum {

  address public solver;

  constructor() public {}

  function setSolver(address _solver) public {
    solver = _solver;
  }

  /*
    ____________/\\\_______/\\\\\\\\\_____        
     __________/\\\\\_____/\\\///////\\\___       
      ________/\\\/\\\____\///______\//\\\__      
       ______/\\\/\/\\\______________/\\\/___     
        ____/\\\/__\/\\\___________/\\\//_____    
         __/\\\\\\\\\\\\\\\\_____/\\\//________   
          _\///////////\\\//____/\\\/___________  
           ___________\/\\\_____/\\\\\\\\\\\\\\\_ 
            ___________\///_____\///////////////__
  */
}

Solidity Concepts

Contract creation bytecode

Smart contracts run on the Ethereum Virtual Machine (EVM). The EVM understands smart contracts as bytecode. Bytecode is a sequence of hexadecimal characters:
0x6080604052348015600f57600080fd5b5069602a60005260206000f3600052600a6016f3fe.
Developers on the other hand, write and read them using a more human readable format: solidity files.

The solidity compiler digests .sol files to generate:

  • contract creation bytecode: this is the smart contract format that the EVM understands
  • assembly code: this is the bytecode as a sequence of opcodes. From a human point of view, it is less readable that Solidity code but more readable than bytecode.
  • Application Binary Interface (ABI): this is like a customized interpret in a JSON format that tells applications (e.g a Dapp making function calls using web3.js) how to communicate with a specific deployed smart contract. It translates the application language (JavaScript) into bytecode that the EVM can understand and execute.

Contract creation bytecode contain 2 different pieces of bytecode:

  • creation code: only executed at deployment. It tells the EVM to run the constructor to initialize the contract and to store the remaining runtime bytecode.
  • runtime code: this is what lives on the blockchain at what Dapps, users will interact with.

contract creation workflow diagram

EVM = Stack Machine

As a stack machine, the EVM functions according to the Last In First Out principle: the last item entered in memory will be the first one to be consumed for the next operation.
So an operation such as 1 + 2 * 3 will be written 3 2 * 1 + and will be executed by a stack machine as follows:

Stack Level Step 0 Step 1 Step 2 Step 3 Step 4 Step 5 Step 6
0 3 2 * 6 1 + 7
1 3 2 6 1
2 3 6

In addition to its stack component, the EVM has memory, which is like RAM in the sense that it is cleared at the end of each message call, and storage, which corresponds to data persisted between message calls.

OPCODES

How do we control the EVM? How do we tell it what to execute?
We have to give it a sequence of instructions in the form of OPCODES. An OPCODE can only push or consume items from the EVM’s stack, memory, or storage belonging to the contract.
Each OPCODE takes one byte.
Each OPCODE has a corresponding hexadecimal value: see the opcode values mapping here (from pyevm) or in the Ethereum Yellow Paper - appendix H.
So "assembling" the OPCODES hexadecimal values together means reconstructing the bytecode.
Splitting the bytecode into OPCODES bytes chunks means "disassembling" it.

For a more detailed guide on how to deconstruct a solidity code, check this post by Alejandro Santander in collaboration with Leo Arias.

Hack

  1. Runtime code

    # (bytes) OPCODE Stack (left to right = top to bottom) Meaning bytecode
    00 PUSH1 2a push 2a (hexadecimal) = 42 (decimal) to the stack 602a
    02 PUSH1 00 2a push 00 to the stack 6000
    05 MSTORE 00, 2a mstore(0, 2a), store 2a = 42 at memory position 0 52
    06 PUSH1 20 push 20 (hexadecimal) = 32 (decimal) to the stack (for 32 bytes of data) 6020
    08 PUSH1 00 20 push 00 to the stack 6000
    10 RETURN 00, 20 return(memory position, number of bytes), return 32 bytes stored in memory position 0 f3

    The assembly of these 10 bytes of OPCODES results in the following bytecode: 602a60005260206000f3

  2. Creation code
    We want to excute the following:

    • mstore(0, 0x602a60005260206000f3): store the 10 bytes long bytecode in memory at position 0.
      This will store 602a60005260206000f3 padded with 22 zeroes on the left to form a 32 bytes long bytestring.
    • return(0x16, 0x0a): starting from byte 22, return the 10 bytes long runtime bytecode.
    # (bytes) OPCODE Stack (left to right = top to bottom) Meaning bytecode
    00 PUSH10 602a60005260206000f3 push the 10 bytes of runtime bytecode to the stack 69602a60005260206000f3
    03 PUSH 00 602a60005260206000f3 push 0 to the stack 6000
    05 MSTORE 0, 602a60005260206000f3 mstore(0, 0x602a60005260206000f3)0 52
    06 PUSH a push a = 10 (decimal) to the stack 600a
    08 PUSH 16 a push 16 = 22 (decimal) to the stack 6016
    10 RETURN 16, a return(0x16, 0x0a) f3
  3. The complete contract creation bytecode is then 69602a60005260206000f3600052600a6016f3

  4. Deploy the contract with web3.eth.sendTransaction({ data: '0x69602a60005260206000f3600052600a6016f3' }), which returns a Promise. The deployed contract address is the value of the contractAddress property of the object returned when the Promise resolves.

  5. Pass the address of the deployed solver contract to the setSolver function of the MagicNumber contract.

Takeaways

Having an understanding of the EVM at a lower level, especially understanding how contracts are created and how bytecode can be dis/assembled from/to OPCODES is benefetial to smart contract developers in several ways:

  • better debugging
  • possibilities to finely optimize contract runtime or creation code

However both operations, assembling OPCODES into bytecode or disassembling bytecode into OPCODES, are cumbersome and tricky to manually perform without mistakes. So for efficiency and security reasons, developers are better off leaving it to compilers, writing solidity code and working with ABIs!

Solutions on GitHub

Ethernaut: Levels 13 to 15

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Gatekeeper Two
Naught Coin

Level 13 - Gatekeeper One

Target: make it past the gatekeeper.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract GatekeeperOne {

  using SafeMath for uint256;
  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    require(gasleft().mod(8191) == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}

Weakness

  • Contract relies on tx.origin.
  • - Being able to read the public contract logic teaches how to pass gateTwo and gateThree.

Solidity Concepts: explicit conversions and masking

Explicity type conversions

Be careful, conversion of integers and bytes behave differently!

conversion to uint bytes
shorter type left-truncate: uint8(273 = 0000 0001 0001 0001) = 00001 0001 = 17 right-truncate: bytes4(0x1111111122222222) = 0x11111111
larger type left-padded with 0: uint16(17 = 0001 0001) = 0000 0000 0001 0001 = 17 right-padded with 0: bytes8(0x11111111) = 0x1111111100000000

Masking

Masking means using a particular sequence of bits to turn some bits of another sequence "on" or "off" via a bitwise operation.
For example to "mask off" part of a sequence, we perform an AND bitwise operation with:

  • 0 for the bits to mask
  • 1 for the bits to keep
    10101010
AND 00001111
 =  00001010

Hack

  1. Pass gateOne: deploy an attacker contract that will call the victim contract's enter function to ensure msg.sender != tx.origin. This is similar to what we've accomplished for the Level 4 - Telephone
  2. Pass gateTwo
  3. Pass gateThree Note that we need to pass a 8 bytes long _gateKey. It is then explicitly converted to a 64 bits long integer.
    1. Part one
      • uint16(uint64(_gateKey)): uint64 _gateKey is converted to a shorter type (uint16) so we keep the last 16 bits of _gateKey.
      • uint32(uint64(_gateKey)): uint64 _gateKey is converted to a shorter type (uint32) so we keep the last 32 bits of _gateKey
      • uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)): we convert uint16 to a larger type (uint32), so we pad the last 16 bits of gateKey with 16*0 on the left. This concatenation should equal the last 32 bits of gateKey.
      • Mask to apply on the last 32 bits of _gateKey: 0000 0000 0000 0000 1111 1111 1111 1111 = 0x0000FFFF
    2. Part two
      • uint32(uint64(_gateKey): last 32 bits of _gateKey
      • uint32(uint64(_gateKey)) != uint64(_gateKey): the last 32 bits of gateKey are converted to a larger type (uint64), so we pad them with 320 on the left. This concanetation (320-last32bitsofGateKey) should not equal _gateKey: so we need to keep the first bits of _gateKey
      • Mask to apply to keep the first 32 bits: 0xFFFFFFFF
    3. We then concatenate both masks: 0xFFFF FFFF 0000 FFFF Requires keeping the first 32 bits, mask with 0xFFFFFFFF. Concatenated with the first part: mask = 0xFFFF FFFF 0000 FFFF
    4. Part three: uint32(uint64(_gateKey)) == uint16(tx.origin)
      • we need to take _gatekey = tx.origin
      • we then apply the mask on tx.origin to ensure part one and two are correct

Takeaways

  • Abstain from asserting gas consumption in your smart contracts, as different compiler settings will yield different results.
  • Be careful about data corruption when converting data types into different sizes.
  • Save gas by not storing unnecessary values.
  • Save gas by using appropriate modifiers to get functions calls for free, i.e. external pure or external view function calls are free!
  • Save gas by masking values (less operations), rather than typecasting

Level 14 - Gatekeeper Two

Target: make through the gatekeeper.

Contract

pragma solidity ^0.5.0;

contract GatekeeperTwo {

  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    uint x;
    assembly { x := extcodesize(caller) }
    require(x == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
    require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}

Weakness

  • gateOne relies on tx.origin.
  • Being able to reading the public contract logic teaches how to pass gateTwo and gateThree.

Solidity Concepts: inline assembly & contract creation/initialization

From the Ethereum yellow paper section 7.1 - subtleties we learn:

while the initialisation code is executing, the newly created address exists but with no intrinsic body code⁴.
4.During initialization code execution, EXTCODESIZE on the address should return zero [...]

Hack

  1. gateOne: similar to the gateOne of Level 13 - Gatekeeper One or to the hack of Level 4 - Telephone
  2. gateTwo: call the enter function during contract initialization, i.e from within constructor to ensure EXTCODESIZE = 0
  3. gateThree
    • uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) noted a ^ b means a XOR b
    • uint64(0) - 1: underflow, this is equals to uint64(1) So we need to take _gatekey = ~a (Bitwise NOT) to ensure that the XOR product of each bit of a and b will be 1.

Takeaways

During contract initialization, the contract has no intrinsic body code and its extcodesize is 0.

Level 15 - Naughtcoin

Target: transfer your tokens to another address.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';

 contract NaughtCoin is ERC20, ERC20Detailed {

  // string public constant name = 'NaughtCoin';
  // string public constant symbol = '0x0';
  // uint public constant decimals = 18;
  uint public timeLock = now + 10 * 365 days;
  uint256 public INITIAL_SUPPLY;
  address public player;

  constructor(address _player) 
  ERC20Detailed('NaughtCoin', '0x0', 18)
  ERC20()
  public {
    player = _player;
    INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
    // _totalSupply = INITIAL_SUPPLY;
    // _balances[player] = INITIAL_SUPPLY;
    _mint(player, INITIAL_SUPPLY);
    emit Transfer(address(0), player, INITIAL_SUPPLY);
  }

  function transfer(address _to, uint256 _value) lockTokens public returns(bool) {
    super.transfer(_to, _value);
  }

  // Prevent the initial owner from transferring tokens until the timelock has passed
  modifier lockTokens() {
    if (msg.sender == player) {
      require(now > timeLock);
      _;
    } else {
     _;
    }
  } 
} 

Weakness

NaughCoin inherits from the ERC20 contract.
Looking at this contract, we notice that transfer() is not the only function to transfer tokens.

Indeed transferFrom(address sender, address recipient, uint256 amount) can be used instead: provided that a 3rd user (spender) was allowed beforehand by the owner of the tokens to spend a given amount of the total owner's balance, spender can transfer amount to recipient in the name of owner.

Successfully executing transferFrom requires the caller to have allowance for sender's tokens of at least amount. The allowance can be set with the approve or increaseAllowance functions inherited from ERC20.

Concepts: ERC20 token contract

The ERC20 token contract is related to the EIP 20 - ERC20 token standard. It is the most widespread token standard for fungible assets.

Any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a medium of exchange currency, voting rights, staking, and more.

Hack

Architecture

transferFrom calls _transfer and _approve. _approve calls allowance and checks whether the caller was allowed to spend the amount by sender.

architecture diagram

Workflow

We want to set the player's allowance for the attack contract. For this we need to callapprove() which calls _approve(msg.sender, spender, amount). In this call we need msg.sender == player, so we can't call victim.approve() from the attacker contract. If we would, then msg.sender == attackerContractAddress. This would set the attack contract's allowance instead of the player's one.
Finally we let the attacker call transferFrom() to transfer to itself the player's tokens.

Hack workflow diagram

Security Takeaways

Get familiar with contracts you didn't write, especially with imported and inherited contracts. Check how they implement authorization controls.

Solutions on GitHub

Ethernaut: Levels 10 to 12

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Privacy
Elevator

Level 10 - Reentrancy

Target: steal all funds from the contract.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Reentrance {

  using SafeMath for uint256;
  mapping(address => uint) public balances;

  function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
  }

  function balanceOf(address _who) public view returns (uint balance) {
    return balances[_who];
  }

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      (bool result, bytes memory data) = msg.sender.call.value(_amount)("");
      if(result) {
        _amount;
      }
      balances[msg.sender] -= _amount;
    }
  }

  function() external payable {}
}

Weakness

Similarly to the attack in the Level 7 - Force, when sending directly funds to an address, one does not now if it is an POA or a contract, and how the contract the contract will handle the funds.
The fallback could "reenter" in the function that triggered it.
If the check effect interaction pattern is not followed, one could withdraw all the funds of a contract: e.g if a mapping that lists the users' balance is updated only at the end at the function!

Solidity Concepts: "reenter", calling back the contract that initiated the transaction and execute the same function again.

Check also the differences between call, send and transfer seen in Level 7 - Force.
Especially by using call(), gas is forwarded, so the effect would be to reenter multiple times until the gas is exhausted.

Hack

  1. Deploy an attacker contract
  2. Implement a payable fallback that "reenter" in the victim contract: the fallback calls reentrance.withdraw()
  3. Donate an amount donation
  4. "Reenter" by withdrawing donation: call reentrance.withdraw(donation) from attacker contract
  5. Read remaining balance of victim contract: remaining = reentrance.balance
  6. Withdraw remaining: call reentrance.withdraw(remaining) from attacker contract

Takeaways

To protect smart contracts against reentrancy attacks, it used to be recommended to use transfer() instead of send or call as it limits the gas forwarded. However gas costs are subject to change. Especially with EIP 1884 gas price changed.
So smart contracts logic should not depend on gas costs as it can potentially break contracts.
Therefore transfer is then no longer recommended. Source 1 Source 2
Use call instead. As it forwards all the gas, execution of your smart contract won't break.
But if we use call and don't limit gas anymore to prevent ourselves from errors caused by running out of gas, we are then exposed to reentrancy attacks, aren't we?!
This is why one must:

  • Respect the check-effect-interaction pattern.
    1. Perform checks
      • who called? msg.sender == ?
      • how much is send? msg.value == ?
      • Are arguments in range
      • Other conditions...
    2. If checks are passed, perform effects to state variables
    3. Interact with other contracts or addresses
      • external contract function calls
      • send ethers ...
  • or use a use a reentrancy guard: a modifier that checks for the value of a locked bool

Level 11 - Elevator

Target: reach the top of the Building.

Contract

pragma solidity ^0.5.0;


interface Building {
  function isLastFloor(uint) external returns (bool);
}


contract Elevator {
  bool public top;
  uint public floor;

  function goTo(uint _floor) public {
    Building building = Building(msg.sender);

    if (! building.isLastFloor(_floor)) {
      floor = _floor;
      top = building.isLastFloor(floor);
    }
  }
}

Weakness

The Elevator never implements the isLastFloor() function from the Building interface. An attacker can create a contract that implements this function as it pleases him.

Solidity Concepts: interfaces & inheritance

Interfaces are similar to abstract contracts, but they cannot have any functions implemented.
Contracts need to be marked asabstract when at least one of their functions is not implemented.

Contract Interfaces specifies the WHAT but not the HOW.
Interfaces allow different contract classes to talk to each other.
They force contracts to communicate in the same language/data structure. However interfaces do not prescribe the logic inside the functions, leaving the developer to implement it.
Interfaces are often used for token contracts. Different contracts can then work with the same language to handle the tokens.

Interfaces are also often used in conjunction with Inheritance.

When a contract inherits from other contracts, only a single contract is created on the blockchain, and the code from all the base contracts is compiled into the created contract.
Derived contracts can access all non-private members including internal functions and state variables. These cannot be accessed externally via this, though.
They cannot inherit from other contracts but they can inherit from other interfaces.

Hack

  1. Write a malicious attacker contract that will implement the isLastFloor function of the Building interface
  2. implement isLastFloor Note that isLastFloor is called 2 times in goTo. The first time it has to return True, but the second time it has to return False
  3. invoke goTo() from the malicious contract so that the malicious version of the isLastFloor function is used in the context of our level’s Elevator instance!

Takeaways

Interfaces guarantee a shared language but not contract security. Just because another contract uses the same interface, doesn’t mean it will behave in the same way.

Level 12 - Privacy

Target: unlock contract.

Contract

pragma solidity ^0.5.0;

contract Privacy {

  bool public locked = true;
  uint256 public ID = block.timestamp;
  uint8 private flattening = 10;
  uint8 private denomination = 255;
  uint16 private awkwardness = uint16(now);
  bytes32[3] private data;

  constructor(bytes32[3] memory _data) public {
    data = _data;
  }

  function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));
    locked = false;
  }

  /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
}

Weakness

Similarly to the Level 8 -Vault, the contract's security relies on the value of a variable defined as private. This variable is actually publicy visible

Solidity Concepts

The layout of storage data in slots and how to read data from storage with getStorageAt were covered in Level 8 -Vault.

The slots are 32 bytes long.
1 byte = 8 bits = 4 nibbles = 4 hexadecimal digits.
In practice, when using e.g getStorageAt we get string hashes of length 64 + 2 ('0x') = 66.

Hack

  1. Analyse storage layout: |slot|variable| |--|--| |0|bool (1 bit long)| |1|ID (256 bits long)| |2|awkwardness (16 bytes) - denomination (8 bytes) - flattening (8 bytes)| |3|data0| |4|data1| |5|data2|

The _key variable is slot 5.

  1. Take the first 16 bytes of the get: take the first 2 ('0x') + 2 * 16 = 34 characters of the bytestring.

Takeaways

  • Same as for Level 8 -Vault:
    • All storage is publicly visible, even private variables
    • Don't store passwords or secret data on chain without hashing them first
  • Storage optimization
    • Use memory instead of storage if persisting data in state is not necessary
    • Order variables in such way that slots occupdation is maximized.

Less efficient storage layout
More efficient storage layout

Solutions on GitHub

Ethernaut: Levels 7 to 9

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Vault
King

Level 7 - Force

Target: make the balance of the contract greater than zero.

Contract

pragma solidity ^0.5.0;

contract Force {/*

                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)

*/}

Solidity Concept: selfdestruct

3 methods exist to receive Ethers:

  1. Message calls and payable functions
    • addr.call{value: x}(''): returns success condition and return data, forwards all available gas, adjustable
    • <address payable>.transfer(uint256 amount): reverts on failure, forwards 2300 gas stipend, not adjustable
    • <address payable>.send(uint256 amount) returns (bool): returns false on failure, forwards 2300 gas stipend, not adjustable function receive() payable external {}
  2. contract designated as recipient for mining rewards
  3. selfdestruct(address payable recipient): destroy the current contract, sending its funds to the given Address and end execution.

Hack

As the contract to hack has no payable function to receive ether, we send ether to it by selfdestructing another contract, designating the victim contract as the recipient.

Takeaways

By selfdestructing a contract, it is possible to send ether to another contract even if it doesn't have any payable functions.
This is dangerous as it can result in losing ether: sending ETH to a contract without withdraw function, or to a removed contract.

Level 8 - Vault

Contract

Target: Unlock vault.

pragma solidity ^0.5.0;

contract Vault {
  bool public locked;
  bytes32 private password;

  constructor(bytes32 _password) public {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}

Weakness

The unlock function relies on a password with a private visibility. There is no real privacy on Ethereum which is public blockchain. The private visibility parameter is misleading as all data can still be read. Indeed the contract is available, so an attacker can know in which storage slot a variable is stored in and access its value manually using getStorageAt.

Solidity Concepts: storage

Storage: storage vs memory

  • storage: persistent data between function calls and transactions.
    • Key-value store that maps 256-bit words to 256-bit words.
    • Not possible to enumerate storage from within a contract
    • Costly to read, and even more to initialise and modify storage. Because of this cost, you should minimize what you store in persistent storage to what the contract needs to run. Store data like derived calculations, caching, and aggregates outside of the contract.
    • A contract can neither read nor write to any storage apart from its own.
  • memory ~ RAM: not persistent. A contract obtains a freshly cleared instance of memory for each message call

Layout

Data stored is storage in laid out in slots according to these rules:

  • Each slot allows 32 bytes = 256 bits
  • Slots start at index 0
  • Variables are indexed in the order they’re defined in contract contract Sample { uint256 first; // slot 0 uint256 second; // slot 1 }
  • Bytes are being used starting from the right of the slot
  • If a variable takes under < 256 bits to represent, leftover space will be shared with following variables if they fit in this same slot.
  • If a variable does not fit the remaining part of a storage slot, it is moved to the next storage slot.
  • Structs and arrays (non elementary types) always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules).
  • Constants don’t use this type of storage. (They don't occupy storage slots)

Storage layout image

Read storage: web3.eth.getStorageAt

Knowing a contract's address and the storage slot position a variable is stored in, it is possible to read its value value using the getStorageAt function of web3.js.

Hack

  1. Read contract to find out in slot password is stored in:
    • locked bool takes 1 bit of the first slot index 0
    • password is 32 bytes long. It can fit on the first slot so it goes on next slot at index 1
  2. Read storage at index 1
  3. Pass this value to the unlock function ## Takeaways
  4. Nothing is private in the EVMhttps://solidity.readthedocs.io/en/v0.6.2/security-considerations.html#private-information-and-randomness: addresses, timestamps, state changes and storage are publicly visible.
  5. Even if a contract set a storage variable as private, it is possible to read its value with getStorageAt
  6. When necessary to store sensitive value onchain, hash it first (e.g with sha256)

Level 9 - King

Target: Prevent losing kingship when submitting your instance.

Contract

pragma solidity ^0.5.0;

contract King {

  address payable king;
  uint public prize;
  address payable public owner;

  constructor() public payable {
    owner = msg.sender;  
    king = msg.sender;
    prize = msg.value;
  }

  function() external payable {
    require(msg.value >= prize || msg.sender == owner);
    king.transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

  function _king() public view returns (address payable) {
    return king;
  }
}

Weakness

The contract uses transfer instead of a withdraw pattern to send Ether.

Solidity Concepts: sending and receiving Eth

  • Neither contracts nor “external accounts” are currently able to prevent that someone sends them Ether. Contracts can react on and reject a regular transfer
  • If a contract receives Ether (without a function being called), either the receive Ether or the fallback function is executed. If it does not have a receive nor a fallback function, the Ether will be rejected (by throwing an exception).

Hack

Upon submission, the level contract sends an Ether amount higher than prize to the contract instance contract fallback to reclaim kingship. The fallback uses transfer to send the prize value to the current king which about to be replace. Only then the king address is updated. If the current king is a contract without a fallback or receive function execution will fail before the king address can be updated.

  1. Deploy a malicious contract without neither a payable fallback nor a payable receive function
  2. Let this malicious contract become king by sending Ether to the vKing contract
  3. Submit instance

Takeaways

  • Assume any external account or contract you don't know/own is potentially malicious
  • Never assume transactions to external contracts will be successful
  • Handle failed transactions on the client side in the event you do depend on transaction success to execute other core logic.

Especially when transferring ETH:

  • Avoid using send() or transfer(). If using send() check returned value
  • Prefer a 'withdraw' pattern to send ETH

Solutions on GitHub

Ethernaut: Levels 4 to 6

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

go to:
Token
Delegation

Level 4 - Telephone

Target: claim ownership of the contract.

Contract

pragma solidity ^0.5.0;

contract Telephone {

  address public owner;

  constructor() public {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {
      owner = _owner;
    }
  }
}

Weakness

A conditional requirements uses tx.origin

Solidity Concepts: tx.origin vs msg.sender

  • tx.origin (address payable): sender of the transaction (full call chain)
  • msg.sender (address payable): sender of the message (current call)

In the situation where a user call a function in contract 1, that will call function of contract 2:

at execution contract 1 at execution in contract 2
msg.sender userAddress contract1Address
tx.origin userAddress userAddress

Hack

Deploy an attacker contract.
Call the changeOwner function of the original contract from the attacker contract to ensure tx.origin != msg.sender and pass the conditional requirement.

Takeaways

Don't use tx.origin

Level 5 - Token

Target: "You are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens."

Contract

pragma solidity ^0.5.0;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

Weakness

An sum operation is performed but overflow isn't checked for.

Solidity Concepts: bits storage, underflow/overflow

Ethereum’s smart contract storage slot are each 256 bits, or 32 bytes. Solidity supports both signed integers, and unsigned integers uint of up to 256 bits.
However as in many programming languages, Solidity’s integer types are not actually integers. They resemble integers when the values are small, but behave differently if the numbers are larger. For example, the following is true: uint8(255) + uint8(1) == 0. This situation is called an overflow. It occurs when an operation is performed that requires a fixed size variable to store a number (or piece of data) that is outside the range of the variable’s data type. An underflow is the converse situation: uint8(0) - uint8(1) == 255.
over/underflow image

Hack

Provoke the overflow by transferring 21 tokens to the contract.

Takeaways

Check for over/underflow manually:

if(a + c > a) {
  a = a + c;
}

Or use OpenZeppelin's math library that automatically checks for overflows in all the mathematical operators.

Level 6 - Delegation

Target: claim ownership of the contract.

Contract

pragma solidity ^0.5.0;

contract Delegate {

  address public owner;

  constructor(address _owner) public {
    owner = _owner;
  }

  function pwn() public {
    owner = msg.sender;
  }
}

contract Delegation {

  address public owner;
  Delegate delegate;

  constructor(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);
    owner = msg.sender;
  }

  function() external {
    (bool result, bytes memory data) = address(delegate).delegatecall(msg.data);
    if (result) {
      this;
    }
  }
}

Weakness

The Delegation fallback implements a delegatecall.
By sending the right msg.data we can trigger the function pwn() of the Delegate contract. Since this function is executed by a delegatecall the context will be preserved:
owner = msg.sender = address of contract that send data to the Delegation fallback (attacker contract)

Solidity Concepts: storage, call another contract's function

There are several ways to interact with other contracts from within a given contract.

If ABI available

If the ABI (like an API for smart contract) and the contract's address are known, we can simply instantiate (e.g with a contract interface) the contract and call its functions).

contract Called {
     function fun() public returns (bool);
}

contract Caller {
     Called public called; 
     constructor (Called addr) public {
         called = addr;
    }

    function call () {
      called.fun();
    }
}

ABI not available: delegatecall or call

Calling a function means injecting a specific context (arguments) to a group of commands (function) and commands are executing one by one with this context.

Bytecode

In Ethereum, a function call can be expressed by a 2 parts bytecode as long as 4 + 32 * N bytes.

  • Function Selector: first 4 bytes of function call’s bytecode. Generated by hashing target function’s name plus with the type of its arguments excluding empty space. Ethereum uses keccak-256 hashing function to create function selector: functionSelectorHash = web3.utils.keccak256('func()')
  • Function Argument: convert each value of arguments into a hex string padded to 32bytes.

If there is more than one argument, they are concatenated.
In Solidity encoding the function selector together with the arguments can be done with abi.encode, abi.encodePacked, abi.encodeWithSelector and abi.encodeWithSignature:
abi.encodeWithSignature("add(uint256,uint256)", valueForArg1, valueForArg2)

Call: doesn't preserve context.

Can be used to invoke public functions by sending data in a transaction.
contractInstance.call(bytes4(keccak256("functionName(inputType)"))
call diagram

DelegateCall: preserves context

contractInstance.delegatecall(bytes4(keccak256("functionName(inputType)"))
Delegate calls preserve current calling contract's context (storage, msg.sender, msg.value).
The calling contract using delegate calls allows the called contract to mutate its state.
delegatecall diagram
delegatecall mtating state diagram 2

Hack

  1. Compute the encoded hash that will be used for msg.data
  2. Send msg.data in a transaction to the contract fallback

Takeaways

  • Use the higher level call() function to inherit from libraries, especially when you don’t need to change contract storage and do not care about gas control.
  • When inheriting from a library intending to alter your contract’s storage, make sure to line up your storage slots with the library’s storage slots to avoid unexpected state changes..
  • Authenticate and do conditional checks on functions that invoke delegatecalls.

Solutions on GitHub

Ethernaut: Levels 1 to 3

The Ethernaut is a Web3/Solidity based wargame inspired from overthewire.org, played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

goto:
Fallout
Coin Flip

Level 1 - Fallback

Target: claim ownership of the contract & reduce its balance to 0.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallback {

  using SafeMath for uint256;
  mapping(address => uint) public contributions;
  address payable public owner;

  constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
  }

  modifier onlyOwner {
        require(
            msg.sender == owner,
            "caller is not the owner"
        );
        _;
    }

  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

  function getContribution() public view returns (uint) {
    return contributions[msg.sender];
  }

  function withdraw() public onlyOwner {
    owner.transfer(address(this).balance);
  }

  function() payable external {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
}

Weakness

The contract's fallback function can owneship of the contract. The conditional requirements are not secure: any contributor can become owner after sending any value to the contract.

Solidity concept: fallback function

A contract can have at most one fallback function, declared using fallback () external payable. This function cannot have arguments, cannot return anything and must have external visibility. It is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable.
Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.

Hack

  1. Contribute
  2. Send any amount to the contract, which will trigger the fallback.
  3. Conditional requirements will be met
  4. Sender becomes the owner

Takeaways

  • Be careful when implementing a fallback that changes state as it can be triggered by anyone sending ETH to the contract.
  • Avoid writing a fallback that can perform critical actions such as changing ownership or transfer funds.
  • A common pattern is to let the fallback only emit events (e.g emit FundsReceived).

Level 2 - Fallout

Target: claim ownership of the contract.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallout {

  using SafeMath for uint256;
  mapping (address => uint) allocations;
  address payable public owner;


  /* constructor */
  function Fal1out() public payable {
    owner = msg.sender;
    allocations[owner] = msg.value;
  }

  modifier onlyOwner {
            require(
                msg.sender == owner,
                "caller is not the owner"
            );
            _;
        }

  function allocate() public payable {
    allocations[msg.sender] = allocations[msg.sender].add(msg.value);
  }

  function sendAllocation(address payable allocator) public {
    require(allocations[allocator] > 0);
    allocator.transfer(allocations[allocator]);
  }

  function collectAllocations() public onlyOwner {
    msg.sender.transfer(address(this).balance);
  }

  function allocatorBalance(address allocator) public view returns (uint) {
    return allocations[allocator];
  }
}

Weakness

The contract used a syntax deprecated since v 0.5. The function meant to be the constructor isn't one. It can actually be called after contract initialisation. It has a public visibility and can be called by anyone.

Solidity Concept: constructor

A constructor is an optional function declared with the constructor keyword which is executed upon contract creation, and where you can run contract initialisation code.
Before the constructor code is executed, state variables are initialised to their specified value if you initialise them inline, or zero if you do not.

Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax was deprecated and is not allowed anymore in version 0.5.0.

The Fal1out() function was supposed to be named Fallout() and would have been the contract's constructor as syntax previous version 0.5.

Hack

Simply call the Fal1out() function.

Takeaway

  • Work with the lastest compiler versions which are more secure.
  • Listen to the compiler warnings.
  • Do test driven development to detect typos.

Level 3 - Coin Flip

Target: guess the correct outcome 10 times in a row.

Contract

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract CoinFlip {

  using SafeMath for uint256;
  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() public {
    consecutiveWins = 0;
  }

  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number.sub(1)));

    if (lastHash == blockValue) {
      revert();
    }

    lastHash = blockValue;
    uint256 coinFlip = blockValue.div(FACTOR);
    bool side = coinFlip == 1 ? true : false;

    if (side == _guess) {
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;
      return false;
    }
  }
}

Weakness

The contract tries to create randomness by relying on blockhashes, block number and a given FACTOR. This data isn't secret:

  • blockhash() and block.number are global variables in solidity
  • the FACTOR used to compute the coinFlip value can be reused by the attacker

Solidity Concepts

blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks
block.number (uint): current block number

Hack

Deploy an attacker contract:
The attacker contract compute itself the blockValue by using the block.number and blockhash() global variables.
As FACTOR is known, the attacker contract can next computecoinFlip and side.
We pass the right side argument to the original flip function that we call from the attacker contract.

Takeaways

There’s no true randomness on Ethereum blockchain, only "pseudo-randomness": random generators that are considered “good enough”.
There currently isn't a native way to generate them.
Everything used in smart contracts is publicly visible, including the local variables and state variables marked as private.
Miners also have control over things like blockhashes, timestamps, and whether to include certain transactions - which allows them to bias these values in their favor.

Solutions on GitHub

Blockchain privacy technologies serie: Confidential Transactions & Bulletproofs

Introduction

In the most famous blockchains protocols (e.g Bitcoin or Ethereum) addresses are partly obfuscated by the fact that addresses are pseudo anonymous: they aren't directly tied with an identity. However chain analysis can succeed in discovering the identity associated with a given address.
We've seen that for instance CoinJoin offers a solution to break the link between sender and receiver. CoinJoin still provides limited privacy because it doesn't obfuscate the transaction amounts.

Limitations of CoinJoin

While CoinJoin obfuscates the relationship between a sender and a recipient, Confidential Transactions (CTs) obfuscate the content of a transaction together with the recipient's address.
CTs "blind" the amounts: from_address pays ?? to to_address.
CT were invented by G.Maxwell (Link 1, Link 2) and further investigated by Adam Gibson.

CT: how it works

Bitcoin transactions maps inputs amounts from a sender, with outputs amounts that can be redeemed by the receiver.
On the Bitcoin blockchain, the amouts are in clear text.

IN OUT
4€ 2€
1€ 3€

As defined by the protocl, for a transaction to be valid, INPUTS needs to equal OUTPUTS:
4 + 1 == 2 + 3

Confidential transactions abd bulletproofs allow to

  1. hide amounts
  2. check that the hidden inputs and hidden outputs add up.
IN OUT
A C
B D

A + B == C + D

Trick 1: Commitments to hide and bind values

Commitments solve the first part: hiding and binding the amounts. By relying on asymmetric cryptography they let you keep a piece of data secret but commit to it so that you cannot change it later.

               commit
data             ->   Commitment(data)
                open
Commitment(data) ->   data

A real-life simple implementation of a commitment could be a sealed enveloppe.
A and B are playing coin flipping but they are in separate rooms. Only B gets to toss the the coin. Only A gets to call. To avoid disputes (A announcing her call after B flips, or B reporting a wrong result):

  1. A would commit to her call in a sealed enveloppe.
  2. A doesn't communicate her call to B
  3. B tosses the coin
  4. B reports result (B still ignores her call)
  5. A's commitment is revealed and tells who won

A simple commitment scheme can be constructed using a cryptographic hash: commitment = SHA256(data )

Trick 2: Homomorphic (Pedersen) commitments to perform operations with bound and hidden values

By committing to inputs and outputs values, we've hidden the values. To check that the sum of hidden inputs matches the sum of hidden outputs, we need commitments that are compatible with the addition (and multiplication) operations. We need our commitments to be homomorphic.

A homomorphism is a map between two algebraic structures of the same type (that is of the same name), that preserves the operations of the structures.
f: A -> B
f is an homomorphism if for all x,y of A, f(xy) = f(x)f(y)

The natural logarithm or the exponential functions are homomorphisms.
Pedersen commitments rely on the discrete logarithm and are precisely homomorphic. So they solve the second part: checking that the sum of committed inputs matches the sum of committed outputs.

IN OUT
A = Com(a) C = Com(c)
B = Com(b) D = Com(d)
A + B == C + D <-> Com(a) + Com(b) == Com(c) + Com(d)
               <-> Com(a+b) == Com(c+d)
               <-> a + b == c + d

However, if the value of a transaction is encrypted, how do I know that someone didn't spend money they didn't have or that no money was created out of thin air?
|IN|OUT|
|--|--|
|A = Com(5)|C=Com(-100)|
|B = Com(4)|D =Com(109)|

A + B == C + D <-> Com(5) + Com(4) == Com(-100) + Com(109)
               <-> Com(4 + 5) == Com(-100 + 109)
               <-> 4 + 9 == -100 + 109
               is TRUE!! 100 created out of nowhere!!

More technically, we aren't really dealing with real negative values. We rather want to prevent overflow as we are in a finite field (e.g finite field from 1 to 9999, 1 + 9999 causes overflow to 100000 = 1).
Verifying that hidden inputs match with hidden outputs sum isn't enough. We need to be able to verify that the values remain in a given range to avoid overflow, while still keeping them secret!

Outcome

  • [x] The inputs' commitments match the outputs' commitments only if the inputs match the outputs.
  • [x] Values are hidden.
  • [ ] Values are in a given range

Trick 3: zero knowlege range proofs as a scalar product

We want to transform the verification that a value v is in a given range (2^n) into the verification of a scalar (or 'inner' or 'dot') product that doesn't require revealing v.
We want to have a zero knowledge proof: (1) 0 <= v <= 2^n <-> (2) t = <l, r>.
"Zero knowledge" meaning that knowing that (2) is true tells that (1) is true too but without revealing v.

  1. Break v in bits: 2^n = (2⁰, 2¹, 2², ..., 2^n-1) a_l = vector of bits = (0, 1, 0, 1, ...., 1, 0)

With (3) v = <a_l, (2⁰, 2¹, 2², ..., 2^n-1)> = <a_l, 2^n> then we can verifiy (1) if we can prove that all bits of a_l are real "bits": either 0 or 1.

  1. Prove bits of v are actually bits (0 or 1) f: {0, 1} -> {-1, 0} b -> c = b - 1 0 -> -1 1 -> 0 b is a bit <-> b.f(b) = 0

With (4) a_r = a_l - 1^n, bits of v are actually bits <-> (5) <a_l, a_r> = 0^n.

So (1) 0 <= v <= 2^n <-> (3) & (4) & (5).
We transformed the verification of a range into the verification of 3 equalities!

Trick 4: combine multiple statements into 1 with a "random scalar challenge"

Random scalar protocol: combine 2 statements into 1

A Prover wants to prove A: a= 0 & B: b= 0.
The Verifier provides a "random challenge scalar x" , where x is in Z_p* = [1, 2, ...p].

P <- V: x
a + b*x = 0

Since prover cannot predict x, if the latter statement holds then the first statement holds with a probability (1 - 1/p).

Random scalar challenge to combine n statements into 1

To prove <a, b> = 0^n the verifier provide a random challenge scalar y:

<a, b> = 0^n <-> for each i in [0, n-1], a[i]*b[i] = 0
             <-> a[0]*b[0] + a[1]b[1]*y + a[2]*b[2]*y² + ... + a[n-1] * b[n-1] * y^n-1 = 0
             <-> <<a,b>, y^n> = 0
(We chose different powers of y to avoid that coefficients can cancel each other out)

So we provide 2 random challenge scalars:

  1. Random challenge scalar y to transform (4) and (5) ( 2 times n equalities) into 2 equalities: (4') and (5').
  2. Random challenge z to transform the (3) & (4') & (5') into 1 equation (6)

After some nice algebra we get

(6) <-> <f(a_l), g(a_l)> = h(v, z) = z²v + d(y, z)
    <-> t  = <l, r>

So the verifiation of the range 0 <= v <= 2^n has been reduced to verifying an equality where one side is defined by the random scalar provided by the Verifier, and one other defined by 2 vectors that reveals v.
We can't send l and r over to the verifier because they would discover v because they already know the random scalar z (they sent it to you over!). This would defeat the whole purpose of confidential transaction which is to hide v!
So we use the trick 2 again: with homomorphic commitments we can hide l and r!
<f(a_l), g(a_r)> = h(v) <-> (7) <f(com(a_l)), g(com(a_r))> = h(com(v))

Outcome

  • [x] The inputs' commitments match the outputs' commitments only if the inputs match the outputs.
  • [x] Values are hidden.
  • [x] Values are in a given range

Trick 5: "compressing" with Bulletproofs for better efficiency

Verifying (7) means verifying n equations. Bulletproofs transactions are an optimization of confidential transactions to "compress" the proof. To reduce it to the verification of log_2(n) equations.
The vectors are "cut" recurringly in half to reduce the dimension of the proof till we get only 1 equality to verify. Then we proceed backwards to check the n equalities.

Summary

Confidential Transactions (CT) hide inputs and outputs.

Building block / Mathematical foundation Purpose
Commitments Hide & bind values
Homomorphic (Pedersen) commitments Perform operations while keeping values hidden and bound
Range proofs Prevent accounting overflow (we are on a finite field) and ensure no money is created out of nowhere
Random scalar challenge Transform multiple statements into one, especially transform a range proof into an inner product proof
Bulletproof Optimization (reduce proof size from O(n) to O(log(n)

To go further:
-Fiat-Shamir: how the challenge scalars are generated

Implementations of CTs

Blockstream Logo
Liquid and Elements sidechains by Blockstream.
BEAM logo
Grin Logo

Video explaination of Bulletproof transactions by Cathie Yun

Blockchain mathematical basis: asymmetric cryptography

Introduction

Backup your private key.
Never share your private key.
Not your keys, not your bitcoins.
Send to: Paste public 0x... address
Sign message metamask

Although, the use of jargon has been reduced to improve UX and onboard more users, the terms "private key, public key, public address, signatures" are still common. Users will also encounter mainstream definitions of blockchain and cryptocurrencies mixing the terms "database, cryptography, security, confidentiality".
How does it all fit together?
What is the mathematical rationale behind this proclaimed and proven security?

> Symmetric cryptography
> Assymmetric cryptography
    > Ensure non collusion & "one-way" mathematical properties
    > Hard to hack ~ intractability
    > Bitcoin application
         > Intractability of elliptic curve discrete logarithm Problem
             > Discrete logarithm problem
                 > Algebra
                     > Finite cyclic group
                         > Group
                             > Set
                             > Binary operation
                             > Properties
                 > Application
                     > Elliptic curve over a finite field
                     > Elliptic curve point multiplication
         > Elliptic Curve Signature Algoritm (ECDSA)

1. Symmetric cryptography

Symmetric cryptography
Symmetric cryptography uses the same key to encrypt and decrypt a message. This key has to be shared between the sender and recipient that want to communicate confidentially. This system can only be as secure as the communication channel used to exchange the key.

2. Assymetric cryptography

By contrast, in assymetric or public-key cryptography, 2 different keys are used:

  • public key: can be freely shared
  • private key: kept secret

The generation of such keys depends on cryptographic algorithms based on mathematical problems to produce one-way (or preimage resistant) functions. Effective security only requires keeping the private key private. The public key can be openly distributed without compromising security.
Public-key encryption is like owning a (key, lock) combination and distributing copies of the lock.

  • "sending" = using a copy of someone else's lock to lock your message = encrypting message to send with someone else's public key
  • "receiving" = opening a message locked with a copy of your lock with your key = decrypting received message with your private key

Public-key encryption illustration

Applications

"Hiding": Public-key encryption to ensure confidentiality

To restrict access to a message's content, one can encrypt it using the receiver's public key. That encrypted message can only be decrypted with the receiver's private key. Especially it shall be impossible to deduce the message from its encrypted version.
To ensure this property called "hiding", the mathematical function used to encrypt the message needs to be very hard or impossible to reverse. It needs to be a one-way function. This mathematical one-way (or preimage resistance) property is desirable because confidentiality stems from it.

"Binding": digital signatures to ensure authenticity, non repudiation

Digital signature illustration
Digitally signing is like creating a lock out of the sender's private key and the message to transmit that
is unique,
and that can only be unlocked by the public key associated with the private key that created it.
Any smallest change in the private key or the message would create a different signature/lock. The signature is mathematically bound to both the message and private key it originally was made with.
To be binding, the mathematical function used to generate the the signature need to ensure non collusion . It needs to be injective.

f(x) == f(y) => x = y <-> x != y => f(x) != f(y)

This mathematical non collusion property is desirable because 3 key properties stem from it:

  • message authenticity = integrity: it is very hard to find 2 different messages that generate the same signature out of the same private key. If the signature is valid, the message is authentic.
  • sender authenticity: it is very hard to find 2 different private keys that generate the same signature out of the same message. If the signature is valid, the message has necessarily been signed by the owner of the private key that generated it.

A corollary of sender's authenticity is

  • non repudiation: provided that private key used to digitally sign a message is properly safeguarded by the original owner, the owner cannot dispute the authenticity of the signature. Nobody can forge the signature

Successfully unlocking the message with the sender's public key confirms these 3 properties.

3. Defining "very hard": intractability

THE KEY TO PUBLIC-KEY CRYPTOGRAPHY IS THE INTRACTABILITY OF CERTAIN MATHEMATICAL PROBLEMS.
All the properties introduced above hold only if it is very hard to find colluding elements, reverse the function... etc..
In the context of cryptography what does "very hard" mean?

A problem that can be solved in theory (e.g. given large but finite resources, especially time), but for which in practice any solution takes too many resources to be useful, is known as an intractable problem. [wikipedia]

...so problems which can be solved by brute force but it would take too long. What does too long mean?
Too long means universe-lifetime-long: longer than 13.799±0.021 ×109 years.

For a more rigorous definition, look into computer science courses.

So to guarantee the "hiding" and "binding" properties, we are looking for a mathematical function that poses 2 intractable problems:

  • Finding an input from an output (one-way)
  • Finding 2 colluding inputs (non-collusion)

4. Example: Bitcoin

The Bitcoin protocol leverages the "hiding" and "binding" properties:

  • hiding: "one way" generation of public keys from private keys
  • binding: signatures of transactions to transfer bitcoins

The intractable problems ensuring these properties in the context of Bitcoin are posed by Ellicptic curves.

4.1 Intractability of the Elliptic Curve Discrete Logarithm Problem (ECDLP)

For elliptic-curve-based protocols, it is assumed that finding the discrete logarithm of a random elliptic curve element with respect to a publicly known base point is intractable = infeasible: this is the "elliptic curve discrete logarithm problem" (ECDLP). The security of elliptic curve cryptography depends on the ability to compute a point multiplication and the inability to compute the multiplicand given the original and product points. The size of the elliptic curve determines the difficulty of the problem. [wikipedia]

Discrete logarithm problem

It is the problem of finding solutions x to the equation g^x = h given elements g and h of a finite cyclic group G. [wikipedia]

Finite cyclic group

Cyclic group

group that is generated by a single element. [wikipedia]

Group [wikipedia]

  • set: collection of distinct elements
    • equipped with a binary operation: calculation that combines two elements (called operands) to produce another element (e.g addition, mutiplication...)
    • such that 4 properties are satisfied, noting + the binary operation on a group G
      • closure: (a, b) in G => a + b in G
      • associativity: (a + b) + c = a + (b + c)
      • identiy: it exists an identity element e | a in G => a + e in G
      • invertibility: for all a in G, a has an inverse element i.e. it exists b | a + b = e

Intuitive example of a cyclic group - clock (12PM format): all hours generated by addition of 1 hours, cyclic because all hours value are decreased when higher than 12: 9:00 + 4:00 = 13:00 = 1:00

Application

In the case of the ECDLP, the finite cyclic group chosen is an elliptic curve over a finite field equipped as binary operation with the elliptic curve point multiplication and with infinity as identity element.
Elliptic curve: curve define by y² = x³ +ax +b, where 4a³ + 27b² !== 0. The condition on a and b is to avoid singular points (points of self intersection or points where the tangents of each branch are equal).
Examples of Elliptic curves
Ellipitic curve point multiplication
- addition P + Q = R = take the symmetric point over the horizontal axis of the intersection of the line (PQ) with the elliptic curve
- identity = infinity: indeed P + Q = R => P + Q - R = 0 = infinity ((PQ) crosses the elliptic curve only in a third point R, no fourth intersection point).
- doubling: particular addition case where P = Q, 2 * P = P + P = take the symmetric point over the horizontal axis of the intersection of the tangent in P with the elliptic curve.
- scalar multiplication: addition + doubling, n * P = P + (n-1)P = P + (P + ....(P + (P + P))
Elliptic curve point operation illustration

Elliptic curve over finite field

In the context of Bitcoin or Ethereum or Blockchain protocols, we want to generate address that have a fixed/finite length. More precisely the Bitcoin public key are 512 bits. So we can't work with infinite numbers. This is why we define the elliptic curve over a finite field of integers:
y² mod p = (x³ + ax + b) mod p.
The general point operations definitions remain valid.
In the case of elliptic curves over a finite field, the generator G of the group is called the base point.
The order is the smallest positive number n | n*G = 0 (= infinity) (number of times the point can be added to itself until its slope is infinite, or a vertical line)
Animation elliptic curve over finite field
Here one can draw the points of an elliptic curve over a finite field.

4.2 Elliptic Curve Discrete Signing Algorithm (ECDSA)

The Bitcoin protocol combines the use of an elliptic curve with following parameters (known as secp256k1.

Parameter Value
Elliptic Curve a = 1, b = 7 => y² = x³ + 7
modulo of the field (prime) in hexadecimal 2²⁵⁶ – 2³² – 2⁹ – 2⁸ – 2⁷ – 2⁶ – 2⁴ – 1 = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
G = base point that generates the cyclic group (in hexadecimal) 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
n = order (in hexadecimal) FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141

To go further and read about why and how these parameters were chosen: link, link.

Further notations:

Notation
z message
d private key
Q public key
G base point
n order

Public-key & private-key

The public key is derived from the private key by scalar multiplication of the base point a number of times equal to the value of the private key.
1.Choose randomly a private key (256-bit integer) d
2.Apply elliptic curve scalar multiplication: public key = private key * base point <-> P = d * G

So given a point P (public key), finding x means finding how many times to substract P to itself to land back on G (base point).
This problem is an ECDLP and is intractable.
So it is computationally infeasible to derive the private key corresponding to a given public key.

Signatures

Sign message, starting point: a message and a private key.

1.Select a random (or generated deterministically in a secret way) integer k from [1,n-1] (reminder: n is the order)
2.Calculate by elliptic point scalar multiplication the point (x, y) = k * G (reminder G is the base point)
3.Calculate r = x mod n. If r = 0, back to 1
4.Caculate s = (z + rd)k⁻¹ mod n. If s = 0, return to step 1. (reminder: we are on an elliptic curve over a finite cyclic group, especially over group where invertibility is fulfilled)
5.Signature is the pair (r, s)

Verify signature

Let Bob be the recipient of a message signed by Alice. Bob must have a copy of Alice's public-key curve point Q.

Validity of public key

-[x] Q != 0 (Q is not infinite)
-[x] Q is on the curve (use curve equation)
-[x] n * Q = 0 (base point * Q is infinite)

Validity of signature, starting point: a message, a signature and a public key

1.[x] r and s are integers
2.Calculate u = zs⁻¹ mod n and v = rs⁻¹
3.Calculate the following elliptic curve point operation: C = (x, y) = u*G + v*Q
4.[ ] if (x, y) = 0 (infinite) the signature is invalid
5.[x] if r = x mod n the signature is valid

Correctness of algorithm

Let C be the point u*G + v*Q.

C = uG + vQ
    = uG + vdG (definition of public key: Q = dG)
    = zs⁻¹G + rs⁻¹dG
    = s⁻¹(z + rd)G

Signature valid <-> s = (z + rd)k⁻¹
                <-> C = ((z + rd)k⁻¹)⁻¹(z + rd)G = (z + rd)(z + rd)⁻¹kG = kG
                <-> x = r mod n (definition of r)

Note

I lied for the sake of simplicity. In reality the very first step is to hash the data with SHA256 to generate a number containing the same number of bits (256) as the order of the curve (z = hash(message)). SHA256 also makes the address quantum resistant. Quantum computers could reverse the point scalar multiplication to get the private address. Quantum computers cannot reverse hash functions.

Summary

To fulfill confidentiality, authencity and integrity, we look for mechanisms to ensure "hiding" and binding. We are looking for functions that are collusion and preimage resistant.
Asymmetric cryptography is about relying on the intractability of some mathematical problems to guarantee these properties.
In the case of Bitcoin (or Ethereum) this problem is the Elliptic Curve Discrete Logarithm Problem.
It is the foundation of the generation of public keys from private keys and of digital signatures.

Blockchain privacy technologies serie: Mixing

Introduction

“Where does a wise man hide a leaf? In the forest. But what does he do if there is no forest? He grows a forest to hide it in.” ― G K Chesterton, The Innocence of Father Brown
"Mixing" technologies rely on obfuscation to hide sensitive information (identity, transaction data) and ultimately ensure privacy and anonymity. Obfuscation commonly takes various forms: adding noise to cover conversation, blend in a crowd, create copies or decoys, natural camouflage... In the context of blockchain, obfuscation is implemented using cryptography.

Note

Besides, most of the interesting blockchain' properties relies on cryptography: immutability, security....and privacy.
Cryptography is based on computer science and mathematics, especially probabilities.
Often it won't ensure absolute validity but rather ensure a very high likelihood of validity.
It is not absolutely true to say that a blockchain is immutable, or that a private key can't be deduced from a public key. It is "only" very unlikely. So unlikely that we end up considering that the properties ensured by cryptography are always true.

"Mixing private keys": Ring Signatures

Group signatures

A group signature scheme is a method for allowing a member of a group to anonymously sign a message on behalf of the group.
It can be compared to the use of rubber stamp. Several employees are being delivered a company's stamp/seal. They use it to sign documents. They don't sign in their name but in the name of the company they belong to. It serves as evidence of authenticity while preserving anonymity.

However group signatures requires a group manager. This leads to critical drawbacks.
First, anonymity can be compromised. The group manager can reveal the identity of signer using its group manager's secret key.
Second, signers groups can't be improvised. The group manager is in charge of forming the group.

Ring signatures

Ring signatures are an improvement of group signatures. They don't require a group manager. Especially they guarantee the anonymity of signers. And a group of signers = Ring can be improvised.

Consider a group of n entities.
Each have public/private key pairs, (P1, S1), (P2, S2), ..., (Pn, Sn).
Party i can compute a ring signature σ on a message m, on input (m, Si, P1, ..., Pn).
Anyone can check the validity of a ring signature given σ, m, and the public keys involved, P1, ..., Pn.

Implementations

Monero Logo
Ring signatures only ensure privacy of the sender. Monero goes further with Ring Confidential Transactions that also obfuscate the identity of the recipient and the transaction amounts.

"Mixing coins": CoinJoin

Imagine the following situation:

  • 10 "spenders"
  • 10 "recipients"
  • requirements:
    • [ ] none of the spender wants to be tied to the recipient after sending their 1€
    • [ ] each spender spends 1 €
    • [ ] each recipient receives 1€

CoinJoin follows this protocol to fulfill the privacy requirement:

  1. All spenders put their one 1€ coin in a bag
  2. Each recipient pulls out 1€ coin out of the bag.
  3. [x] each spender spent 1€
  4. [x] each recipient received 1€
  5. [x] no way to tell where any of the 1€ coins came from

In more technical terms, CoinJoin is a special kind of Bitcoin transaction that breaks the common input ownership euristic. Bitcoin transactions are made of inputs and outputs. Inputs being unspent outputs (UTXO) of previous transactions.
Bitcoin Transaction
Usually all the inputs belong to one same address. CoinJoin breaks this rule. It mixes inputs from different addresses to prevent associating sender and recipient addresses. Instead of performing 1-to-1 payments, we perform m-to-n.

Implementations

Wasabi wallet
Skycoin
Dash

zk-SNARK & Mixing on Ethereum: Tornado Cash

Tornado Cash logo
CoinJoin mixes bitcoins. Tornado Cash mixes ethers using a smart contract.
This "mixer" smart contract collects deposits from a depositary, transfers deposits to withdrawer, and prevents linking depositary and withdrawer thanks to zk proofs.

  1. Deposit
    • A secret is generated
    • The hashed secret (="commitment") is sent to the mixer smart contract along with an amount of n-ETH
    • From now on, this unspent commitment, just like an Bitcoin UTXO, is waiting to be withdrawn/spent. It is mixed to other n-ETH deposits
  2. Withdraw For the smart contract to allow the withdrawal, the withdrawer needs to prove ownership of a secret corresponding to an unspent commitment. zkSNARKs allows doing it without revealing which deposit the secret was generated from.

Money Streaming

"When you change the medium, the message changes"

Said Bitcoin Evangelist Andreas M. Antonopoulos in this talk.

The term "streaming" was coined when Internet superseded physical containers (CDs & VHS) as main 'container' for audiovisual content.

Product/Industry Old medium Today
Music CD, Vynils, tapes Streaming
Video VHS, tapes Streaming
News Newspapers, print outs, books, radio Blog posts, tweet, podcasts

Behaviours regarding the consumption of music and video were conditioned by our perception of their medium/containers.
The physical constraints of the containers translated in framed definitions and limited opportunities:

  • Format and quality were conditioned by the medium: albums had to "fit" on vynils or CDs, films had to fit on a VHS or DVD. Short movies were not produced. Too short meant waste.
  • Buyers valued the object. They cared about the ownership of the container.
  • Production of new content was conditioned by the production costs of the medium: press the vinyls, manufacture the VHS, print the newspapers/books.
  • Distribution was also conditioned by the medium.

When Internet became the new medium, it lifted off the limitations of the previous ones and created unexpected opportunities. As production costs decreased and distribution got easier, new formats became popular. People started valuing shorter videos (30s, 20s, 10s, GIFS...). Short films or even clips of a few seconds get millions of views nowadays. Behaviors changed: people moved from valuing ownership to valuing experience: listening over owning a CD, watching over owning a VHS.

Money Streaming

Music, Video, News...
What about money?
What are the current mediums and containers of money?
Cash bills and coins, bank transfers, bills with fixed amount, monthly invoices, monthly payrolls...

The current money mediums present limitations.

  • Bills and coins can't be carried conveniently in high amounts. They have fixed denominations. Can be destroyed.
  • Banks have high operating costs and involve inefficient processes, can't run 24/7, can't fully rely on automation
  • Invoices and payrolls have to be manually created, edited, reviewed...

The limitations of the money mediums lead to pratices that are annoying and inconvenient for the users.

  • Having to go to an ATM to get cash
  • Bank transfers not processed over weekends
  • Subscriptions that can't be cancelled at anytime (notice period) because of the whole administrative process involved
  • Income and rent paid once a month
  • Interest on savings account, or dividends paid once a year
  • Paying for more than what you actually get

Why is payday once a month? Why isn't pay DAY every DAY?
What if it would be possible to make payment on seconds or milliseconds frequency?
Cryptocurrencies on blockchain networks as new money mediums make this possible. Especially thanks to the latest scalability solutions (Layer 2 solution like Lightning for Bitcoin; Sharding, MATIC ... on Ethereum, zk SNARKs).
The changes in behaviours would be bigger than what we experienced with video and music streaming. The opportunities would be bigger too.
Any time based service could be transformed. Some are even already built on Ethereum.

Applications

Lending and credit

Compound offers interest saving accounts where interests are paid every 15 seconds. Compound made then the creation of an innovative lottery system possible: PoolTogether. PoolTogether is a weekly lottery pool. Tickets sales are invested in the Compound protocol. Winner of the lottery earned the weekly accrued interest only. All loser participants get their money back: a lottery system where nobody lose.

Salaries

With Sablier it is "Payday, Everyday".
"On Sablier, time means money, literally. As a worker, you see your earnings increasing in real-time in the Sablier wallet. As an organisation, [the] technology helps you get rid of the hassle of payroll admin. After a one-time deposit, [the] smart contracts will start "streaming" the money towards the payees, without you lifting a finger again."

Concerts & shows

Instead of a price per ticket, one could imagine a price per second. Who has never been disappointed by an artist ending the show after less time than expected?
With money streaming a shorter show means that the concert price decreases accordingly for the fans. A longer show means more revenues for the artist.

Attention & advertising

Every second of ads watched could be monetized. Today it is done already only for the advertisers who pay for a broadcast time. Not for the viewers of the ads.
With Brave Browser and Brave rewards, the viewer's attention is valuable. Users can "earn by viewing privacy-respecting ads and pay it forward to support content creators [they] love."

Real Estate

On realT, houses are first "tokenized". It enables fractional ownership. Then with "money streaming" blockchains, you no longer need to wait 30 days to receive a bank transfer.
"Owning property with RealT allows you to collect rent every day. Rent is paid using a US-Dollar stablecoin" (DAI).

Other assets renting (cars, rooms...)

Cars could be rented per seconds instead of on a day basis. No need to worry about having to pay an extra day because you returned your car too late.
Same for hotels: stay a little longer, pay more; stay shorter, pay less. Although in the hotel industry the constraints about the current pratices doesn't only come from the money medium but also from e.g the need to plan room cleaning etc...)

Subscriptions and bills

Combined with Internet of Things devices that would track consumption, money streaming would enabled utility bills on a second basis. Or it would be possible to "pause" your consumption.
Holiday scenario:
I go on holidays, I pause my utility bills (gas, electricity, home internet) to have more cashflow on holidays. I board a plane. I know I can't use my phone for calls or browsing anyway. I pause my phone subbscription. During 7 hours I have more cashflowI can "flow" this cash back in on e.g on board entertainment offers.

# phone subcription
10€/month ~ 0.014€/hour
# Home Internet subscription
20€/month ~ 0.028€/hours
# bills
120€/month ~ 0.17/hour
# total saved in 7 hours
(0.014 + 0.028 + 0.17) * 7 ~ 1.5 €

Video and music

Business models would switch from pay per click to pay per seconds viewed.

Conclusion

The convergence of old music & video mediums towards Internet had a major impact of both consumers and producers behaviors.
Cryptocurrencies powered by blockchain networks are becoming a new medium of exchange. Behaviors with regards to money - spending, earning, saving, investing - will be radically changed.

Blockchain privacy technologies serie: Introduction - the information asymmetry threat

Privacy matters

It makes possible for us to develop as free-thinking, independent individuals. One common nonsense argument against the importance of privacy is:
I have nothing to hide, so why should I care about my privacy?
Following this logic we go on declaring:
I having nothing to say, so why should I care about my freedom of speech?
There is nowhere I want to move/travel to, so why should I care about my freedom of movement?
I don't believe in any gods, so why should I care about freedom of religion?
And so on... for any basic human rights.

These rights were granted by Law in order to protect from specific threats. What about privacy?
Privacy encompasses many forms and concerns:

  • integrity of family life
  • state oppression
  • value of data
  • true inner self that can only emerge in anonymity or surveillance that causes behavior changes
  • financial concerns:
    • confidentiality of business transactions: hide negotiated prices, avoid signaling when trading
    • security: does not expose the value of your assets to avoid becoming a target for hackers and thieves
    • loss of fungibility: coins are flagged as unacceptable and can't be spent

Our scope is reduced to data and information privacy.

Data privacy & information asymmetry

This form of privacy protects from the information asymmetry threat. Dealing with data privacy we can distinguish:

  • known knowns: security camera we see on the street
  • known unknowns: what happens with the recording? How long will it be stored?
  • unknown unknowns: all the other uncertainities we are not conscious about because we ignore other possibilites exist: e.g processing the footage with a facial recognition software.

People not realizing the importance of privacy is in itself an evidence of these "unknown unknowns". They are also where the asymmetry comes from.
"Asymmetrical" because:

  • we often have no choice as to whether being monitored.
  • we have little knowledge about how much information is collected.
  • all the work of correlation and analysis is done with tools, techniques or computing power unknown or inacessible to us.
  • the predictive outcomes will turn into decisions felt as arbitrary by the people impacted: deny employment, deny credit, restrict movements, refuse membership
  • the collection of data in the name of reducing some risks faced by a larger group (e.g the state) produces new risks whose danger citizens take on: censorship, coercion, oppression of minorities...

Essentially "they" know much about us: Have power over us. While we know less about "them" and can hardly respond.

Acceptance or going offline/"opting-out" isn't really a choice.
Furthermore, the immutability, transparency, openness characteristics of (public) blockchain networks make the relationship even more asymmetrical.

Obfuscation and cryptography for more data privacy on blockchain networks

3 aspects of privacy stand out in the context of blockchain technology:

  • identity
  • transaction data
  • total blockchain state

As solutions to anonymize transactions, obfuscate transaction data or blockchain state, I will structure my posts around the following protocols and techniques:

To learn more about information asymmetry or find answers to the ethical questions raised by relying on obfuscation techniques, I recommend reading Obfuscation, A User's Guide for Privacy and Protest, Finn Brunton and Helen Nissenbaum.

Talent Stack

Scott Adams Talent Stack

Most of the time, excellence and greatness are understood as specific: "the fatest...", "the best x-player", "the best in x discipline...", "the expert in field x...".
So the obvious way to become valuable is specialization. This requires a lot a discipline, time, patience and personal drive.
Scott Adams Talent Stack offers a different path. It says that even if your skill level is mediocre, if the mix of skills is right, you can become unique and valuable too.
This path requires is easier to take. The Talent Stack concept helps explaining success in cases where observers might describe it as "surprising".

Examples

Kanye West

I am quite into Hip Hop / RnB music. Although I love his music, I can come up with plenty of other male artists that excel him in specific skills.
But his unique mix of skills lead to his sucess.

Skill Kanye's level not as good as
Rapping ok Eminem, Twista, Yelawolf, Tech 9
Vocoder ok T Pain, Zapp & Roger
Singing can't Timberlake, John Legend, Usher, Chris Brown
Writing/Lyrics ok Andre 3000, 2Pac
Composing/Beat Making ok Madlib, Metro Boomin, Mike Will Made It
Dancing can't Usher, Timberlake, Chris Brown
Business acumen ok Buffet
Social Media Presence good Obama, Rihanna, Bieber
Self promotion ok ?
Fashion ok Beckham?

So he is the best in none of his skills. Most of the time he's good or just good enough. It is in combining all his skills together that he succeeded in becoming so unique.
Same goes for his wife Kim Kardashian West. I've always been somewhat astonished by her rising sucess.

K.Kardashian

Skill
Branding & Marketing Obviously good, she is her own brand
Media Knows how to deal with press, reporters, interviews...
Esthetic Not the most beautiful, but pretty enough
Network Leveraged the connections she built during/after the Ray J sextape episode
Engagement on social media She is maybe not THE best at interacting with and engaging fans online, but she can do it and does it efficiently
Family Spread success to her family
Risk management The sex tape episode was definitely a bold and carefully calculated move...
Confidence
Recruiting She partners with the right people in any industry: fashion, music, media...

Each of single skill alone is not enough. But the combination...
This is how a women went from sex tape to a $377M net worth: the right talent stack.

An further application and illustration of this concept is what Naval Ravikant describes as an "unstoppable" skill set:

The unstoppable skill set: Build & Sell

Naval notes that every successful company, individual or team needs to be good at 2 categories of skills: building and selling.

  • Building: R&D, design, engineering, coding, manufacturing, delivering...
  • Selling: Sales, marketing, communicating, recruiting, PR...

Let's imagine an excellent engineer building amazing products. If he can't market himself or attract customers, nobody will ever learn about how great his products are. No customers. No sales. Engineering requiring lot of focus, time and ressources, it is not sustainable.
Same goes for the other way around. What if you're great marketer? If the product you're selling isn't good, customers won't buy again. You won't earn the ressources necessary to improve your product. Not sustainable either.
You can't be the best at both, but you need a decent skill level in both.

As illustrations, he mentions in his post some famous successful team that were made of a "Builder" and a "Seller" combo: Jobs and Wosniak, Gates and Allen, the usual CEO/CTO combo of any startups...
Then you have unstoppable people such as Elon Musk. People who can do both: build and sell. He is not good enough to design the whole rocket himself, but he is good enough to drive all key technical decisions. So he is Builder. He has an excellent business acumen too, which makes him a Seller too.

So builders should endeavour to become sellers and sellers to become builders?
Reality is a bit unfair.
Bill Gates said: “I’d rather teach an engineer marketing, than a marketer engineering.
A seller will have indeed a harder time learning to build than a builder learning to sell. Learning selling as an engineer can still be challenging. Depending on character and personality, builders have to figure out what they feel more confortable doing. What communication they're best suited for:

  • Person to person: recruiting, fund raising
  • Writing: blog, articles, tweets
  • Public speaking: make presentations, conferences, workshops
  • Talking: podcasts, videos
  • Photos