Libraries on Ethereum
First off, there's 3 ways to call a function on a contract. CALL
, CALLCODE
, and DELEGATECALL
.
Libraries on Ethereum are largely implemented with DELEGATECALL
. Meaning, you deploy a contract that serves as a library - it's got some functions that anyone can call, and can even change the storage of the calling contract.
Solidity has some syntactic sugar that lets you declare a library
, which does all the DELEGATECALL
s for you if you use the library in your contract. But on the EVM level it is the same as deploying them as contract
s.
Parity's library contract
Parity initially deployed a mutisig contract as their library. The library itself is a very much working multisig wallet. On a hindsight it probably shouldn't be.
Then all the other multisig wallets have this in their code:
address constant _walletLibrary = 0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4
Since it's a constant, it's populated at compile time, meaning it's permanently stored in "code", not "storage". The value would be the address of the library to DELEGATECALL
on. If you run eth.getCode(walletAddress)
on one of the affected wallets, you should see the address of the now-dead library at index 422.
On another hindsight, it should probably allow users to change the linked library address, instead of coding it in the bytecode.
Killing the library contract
Since the library itself is a working wallet, and it was left uninitialized, anyone would be able to call initWallet
on the library contract. The "hacker" did it in this transaction. The call is perfectly legal, zero tricks involved, since he's just initializing a wallet that's not been initialized.
Now that he is the owner of the library contract, he can call any privileged function he wants. So he called kill
in this transaction. The kill
function basically calls suicide
(which is now being replaced by selfdestruct
, they do the same thing). suicide
destroys the contract, clears its storage and code, sending remaining funds to the owner. (Which is zero, since the library contract holds no funds)
Code becomes zero
Now every function call to the now-dead library will simply return false
, or 0
.
All the multisig wallet contracts that rely on the library contract code would now get zero when they DELEGATECALL
. The multisig wallet contracts still hold funds, they have not been hacked, but all the library code is zeroed out.
The multisig wallets are essentially locked. Most of the functionalities (including the kill-switch function) rely on the library which is now returning zero for every function call.
The only way to resume their functionalities would be a hard fork to add the library code back.
Can you statically link the libraries?
It is very possible to contain all the library code in the multisig wallet contract itself, instead of doing DELEGATECALL
s to an external contract. This is similar to static linking in a sense. It will increase the gas cost of contract deployment though.