The Blockchain Tiramisu
Tech Stacks and Pragmatic Engineering:
deep dive into blockchain engineering practices
About me
- Hacking since age 11
- Blockchain startup in 2015 (before Solidity was stable)
- Contracting for BlockLab - senior blockchain/full-stack engineer
What's a tech stack?
Web 2.0
What's a tech stack?
Frontend
view, controller: React, Vue.js
model: Redux, MobX
API: REST, GraphQL
Backend
framework - Express, Django, Rails
database - ORM
users/auth/permissions
And then blockchain walked in...
Web 3.0
Frontend
MVC, API to backend
Backend
Framework, database, users/auth
Blockchain
Cryptographic identity
Smart contracts
API's
P2P storage
What's a tech stack?
Web 3.0
Frontend
MVC, API to backend
Backend
Framework, database, users/auth
Blockchain
Cryptographic identity
Smart contracts
API's
P2P storage
Is this [still] a stack?
Is this a tech stack?
No.
[Pancake] Stack | Tiramisu |
---|---|
Clear coupling between layers | Delicately assembled pieces |
Clear interaction boundaries | Cream goes wherever |
Served on a plate | Served/contained in many shapes/sizes |
The Blockchain Tiramisu
Today you will understand some implications of engineering the blockchain tiramisu.
The space is nascent.
Blockchain engineering requires learning.
You need to be agile with developing products...
Ingredients
- Ethereum node client (geth/Ganache/Parity)
- EVM (state)
- Consensus engine (mining speed)
- Mining
- Accounts / wallets
- Smart contracts
- Smart contract artifacts (ABI's)
- Smart contract wrappers (TypeScript)
- Ethereum RPC client (Web3)
- Debugging (Remix)
- Frontend integration (Metamask)
- API layer - encoding/decoding data (ABIEncoder, BigNumber.js, hex normalisation)
Issue #1- leaky abstractions
- Ethereum = world computer
- Data is stored in a big Merkle tree
- Rules of updating that data are the EVM
- The EVM processes transactions
- Transactions are (from, to, amount, data)
Implication:
- contract creation is sending ether to a new address
- contract calls are sending ether to an address
- errors get weird...
Issue #1- leaky abstractions
- When a transaction does not work, EVM revert's the state.
- "revert" is an opcode that is shared between the EVM's internal errors and your smart contract's errors
- Implication: there are no stack traces.
- Implication: unit testing is mandatory
Issue #1- leaky abstractions
- Issues you will encounter that don't make sense outside of the blockchain context:
- No more than 16 variables - stack too deep
- Contract code has a maximum size of 24kB, for DoS reasons
- Deploying a contract with { optimize: false } might fail! With a poor poor error!
- Using the Remix debugger is the only way to discover this...
Issue #2- new paradigms
- World computer = BIG integers (256 bits)
- The EVM word size is 256 bits...
- all data passed around is encoded in 256 bit words internally
- tightly vs. full packed
- Implication: dirty higher order bits
- Implication: SQL-injection style "short address attack" based on argument padding
- Implication: parsing data types became harder...
Issue #2- new paradigms
- Contracts = Accounts
- Contracts can own ether (even before they're created)
- Contracts can receive ether (fallback function)
- But they need gas to run
- gas is converted based on gas price
- Estimating gas for calls is a matter of solving the halting problem (but it's okay thusfar)
Issue #2- new paradigms
- Contracts = Accounts
- Contracts can own ether (even before they're created)
- Contracts can receive ether (fallback function)
- But they need gas to run
- gas is converted based on gas price
- Estimating gas for calls is a matter of solving the halting problem (but it's okay thusfar)
Issue #3 - new patterns
- Contract registries
- Upgradeability
- No built-in schema migrations.
- Oracles
- Timed events
- Contract logs / events
- Call vs. send for validation/computation
Issue #4 - integration
Integration with databases - nothing supports uint256.
Implication: no sorting functions available without extra work
Hex addresses:
0xabc vs. 0xABC
Both the same data, but different format = normalisation woes.
Issue #4 - integration
Different pieces of infrastructure:
Ganache blockchain
Geth blockchain
Metamask provider
Different pieces of infrastructure become out of sync
- Caching gasPrice and contract ABI's
- Errors like: Error: insufficient data for uint256 type (arg="_value", coderType="uint256", value="0x")
Issue #5 - reinvention
Client library and usage styles:
- Web3 v0.3 (Node.js callbacks), v1.0 (bespoke PromiseEmitters)
- Truffle client (Web3 v0.3 but with promises)
- TypeScript client
Issue #5 - reinvention
Data coding:
-
Structs are only supported in a new version of ABI coder - it currently returns tuples
-
Cannot return structs from external methods
Issue #5 - reinvention
Data coding:
- No native decimal types - implement yourself
- solution - represent decimals as hex strings
- padLeft the hex strings with zeroes
- but what about decimal hex strings?
- need to call toFraction
- but what about incorrect precision e.g
- Error: String is larger than length we are padding it to: 65 > 16
String: 189d89d89d89d89d89d89d89d89d89d89d89d89d89d89d89d89d89d89d89d
Issue #5 - reinvention
Very very very basic composability
- Libraries, structs and contracts
- Like using K&R C from the 90's
- No such thing as BufferedStreamReader
- No functional programming or automatic variable typing
- DELEGATECALL/STATICCALL instead of passing first-class functions etc.
What can you do?
Use solc-compiler - only recompile when changed
Use abi-gen - typed contract bindings
Study existing project patterns, don't reinvent the wheel
- 0x units
- 0x providers
Unit test
Integration test
Use the Remix IDE for debugging
Questions/anxieties?
The Blockchain Tiramisu
By Liam Zebedee
The Blockchain Tiramisu
Tech Stacks and Pragmatic Engineering: deep dive into blockchain engineering practices. Presented for the Vierde Vrijdag meetup in The Hague, The Netherlands.
- 1,373