Immutability in Go
Post mortem from a DoS-ed blockchain
10 October 2016
dotGo, Paris
Péter Szilágyi
Ethereum Core Developer
Péter Szilágyi
Ethereum Core Developer
Global peer-to-peer networks
5232 Bitcoin nodes (29th September, 2016)
7480 Ethereum nodes (29th September, 2016)
Attempts at uncorruptible shared truths
To infinity... and beyond!
Most toxic ecosystems in technology... (⊙_⊙)
Mt. Gox: Bitcoin's largest heist
TheDAO: Ethereum's largest heist
"Ethereum is a decentralized platform that runs smart contracts: applications that run exactly as programmed without any possibility of downtime, censorship, fraud or third party interference."
19th September, 4 in the morning
Ethereum's current state is a 2.8M node trie
Slight caveats with this model
(1)
(2)
(3)
Slices can change, but strings are immutable¹
Custom structs can be made immutable
Data purity must be preserved
type Gopher struct { name string // Immutable type picture []byte // Mutable type }
func NewGopher(name string, pic []byte) *Gopher { g := &Gopher{ name: name, // Shallow copy immutable picture: make([]byte, len(pic)), // Deep copy mutable } copy(g.picture, pic) return g }
func (g *Gopher) Name() string { return g.name } // Shallow copy immutable func (g *Gopher) Picture() []byte { // Deep copy mutable pic := make([]byte, len(g.picture)) copy(pic, g.picture) return pic }
Arbitrarily complex object hierarchy
Immutable objects need to remain light
type Gopher struct { name string picture []byte parents [2]*Gopher // Lightweight immutables }
func NewGopher(name string, pic []byte, parents []*Gopher) *Gopher { // [...] Init basic fields as previously for i, parent := range parents { // Dereference any pointers cpy := *parent g.parents[i] = &cpy } return g }
func (g *Gopher) Parent(idx int) *Gopher { cpy := *g.parents[idx] // Internal pointers never escape return &cpy }
Updates can be done (only) via recompositions
Mutations always result in new objects
func ExtendFamilyTree(gopher, ancestor *Gopher, ancestry []int) *Gopher { mutable := *gopher // Reconstruct all objects on the update path if len(ancestry) == 0 { // Mutate the object at the deepest level if mutable.parents[0] == nil { mutable.parents[0] = ancestor } else { mutable.parents[1] = ancestor } } else { // Unwind and recombine objects on the update path index := ancestry[0] parent := mutable.parents[ancestry[0]] mutable.parents[index] = ExtendFamilyTree(parent, ancestry[1:], ancestor) } return &mutable }
Lightweight object trees
Data structure immutability is nice
Data structure immutability is hard
Ethereum Core Developer
Gophers drawn by Renee French