Billipede.net

"You just won't believe how vastly, hugely, mind-bogglingly big it is."

filed under:

2014-12-26 Distributed Autonomous Art

The blockchain universe, still mainly a place for blind speculation on the relative values of various digital goods of dubious value (usually by exposing oneself to quite a lot of risk by trusting fly-by-night exchanges held together with chewing gum, PHP, and a prayer) is slowly growing an ecosystem of actually useful infrastructure for the creation of distributed applications other than simply payments. The most general of these is the Turing-complete Ethereum-style smart contracts, which are touted by the project's boosters (and myself) as an epoch-making technology that can help solve lots of important problems in business, government, and society. There are tons of more erudite thinkers that could expand on that and tell you exactly why or why not that might be true, but to be honest that game is a little bit tedious for me. Instead, I thought it would be fun to look at this new medium and do something purposeless with it. In other words, I decided to make some distributed, trustless, autonomous art.
The following contracts are all written in the variant of Serpent accepted by the latest (as of writing) version of PyEthereum (0.7.49).
Here's my first piece. Since blockchain-based technology mainly concerns itself with ownership and commerce, I thought my first Ethereum art piece tshould be a medtiation on the meaning of wonership. What does it mean to own something? More specifically, what does it mean to own something as intanible as a blockchain-based smart contract? To strip it down, I decided that, at minimum, that something needs to 1) be possible to find out who the onwer of something is, and 2) sell it to someone else, who then becomes the owner. Once I'd decided that, I realized that, since my work is software, that these requirements could be written up as a suite of tests, and so wrote a short assert-based Python script to run my tests:
import pyethereum t = pyethereum.tester u = pyethereum.utils s = t.state() c = s.contract('sellmyself.se') seller s.send(t.k0, c, 0, funid0, abi=[]) seller_balance = s.block.get_balance(t.a0) buyer_balance = s.block.get_balance(t.a1) #funid 0 > who_owns_me, 1 > current_price, 2 > reprice, 3 > buy #initial price should be 2^254 assert s.send(t.k0, c, 0, funid1, abi[]) [28948022309329048855892746252171976963317496166410141009864396001978282409984L] #set a new price assert s.send(t.k0, c, 0, funid2, abi[50000000]) [0] #check to make sure it happened assert s.send(t.k0, c, 0, funid1, abi[]) [50000000] #try to set a new price as a different user assert s.send(t.k2, c, 0, funid2, abi[5]) [1] #make sure it failed assert s.send(t.k2, c, 0, funid1, abi[]) != [5] #now the buyer will send the money and hopefull gain ownership: assert s.send(t.k1, c, 50000000, funid3, abi[]) [0] #let's make sure they did: assert s.send(t.k1, c, 0, funid0, abi[]) != seller #seller should be at least 50000000 richer: assert s.block.get_balance(t.a0) >= seller_balance + 50000000 #...and buyer should be at least 50000000 poorer: assert s.block.get_balance(t.a1) <= buyer_balance - 50000000 #price should be at max again: assert s.send(t.k0, c, 0, funid1, abi[]) [28948022309329048855892746252171976963317496166410141009864396001978282409984L] print "all good"
This may be the first instance of TDD as applied to art. Here is the contract that meets those requirements (and passes the tests):
def init(): self.storage[0] = tx.origin #owner of the contract self.storage[1] = 2^254 #price he'll sell it at def who_owns_me(): return self.storage[0] def how_much_i_cost(): return self.storage[1] def change_price(x): if msg.sender != self.storage[0]: return 1 #message sender isn't owner, so don't change anything #they are, so change the price of the contract to what is specified self.storage[1] = x return 0 def buy_me(): if msg.value >= self.storage[1]: send(self.storage[0], self.balance) self.storage[0] = msg.sender self.storage[1] = 2^254 return 0 return 1
Ethereum-style contracts effectively sit on the blockchain and do whatever it is you've programmed them to do. Once you create one, it sits happily forever (unless it decides to "suicide") and runs its code whenever anybody (or any contract) sends it a message and enough "gas" to run itself for that execution. It can then check out the environment by seeing how much money it has, what you said in your message, who sent the message, send messages to other contracts, see what time it is (according to blockchain consensus), the block number and so on, and even send money around to other Ethereum addresses (which can be people or other contracts).
Here, the creator of the contract is also the initial owner, and the first function who\_owns\_me() does just what it says on the box, letting anyone know who owns it. The first requirement is therefore satisfied, since anyone (or any contract) can find out who owns the work. Anybody is free to buy it from him, however, provided they pay the current price. This defaults to 2\^254 Wei, effectively the same as putting something on Ebay with a reserve price of Googol dollars. In other words, it's not really for sale, but the owner can set a lower price when they want to put it up for sale. Once they do, anyone can send another kind of message, and if it contains enough money they are registered as the new oner and the price set back to the maximum.
After that, I started thinking about how I could improve the contract. What are the biggest downsides of buying things? Probably the most onerous is that they don't always increase in value. From art, to houses, to junk bonds, to cars, the biggest drag with buying almost anything is that it could be worth less when you go to sell it again. This is an unassailable problem in the old-and-busted meatspace economy, but with Ethereum we'll finally have the tools to address it. Here's a small variation on the contract that guarantees you can never lose money by selling it for less than what you paid:
First we have to make sure to save the last sale price when a sale occurs, so we'll modify the buy\_me() function slightly:
... def buy_me(): if msg.value >= self.storage[1]: send(self.storage[0], self.balance) self.storage[0] = msg.sender self.storage[2] = self.storage[1] #save the last sale price self.storage[1] = 2^254 #take it off the market for now return 0 return 1 ...
Next, we simply change the change_price() function:
def change_price(x): if msg.sender != self.storage[0]: return 1 #message sender isn't owner, so don't change anything if x < self.storage[2]: return 1 #this is a non-depreciating asset, so we can't sell it for less than we bought it for #everything checks out, so change the price of the contract to what is specified self.storage[1] = x return 0
Notice that we don't have to initialize the self.storage[2] slot to zero, since this is explicitly done as part of the EVM spec. This may seem slimy to C programmers, but is perfectly valid, and the serpent compiler won't optimize it out, at least not currently. I did actually check:
$ cat init_zero.se def init: self.storage[0] = 0 return 0 def dummy: return 0 $ cat dont_init_zero.se def init: return 0 def dummy: return 0 $ serpent pretty_compile init_zero.se | fold [PUSH1, 0, PUSH1, 0, SSTORE, PUSH1, 0, PUSH1, 32, MSTORE, PUSH1, 32, PUSH1, 32, RETURN, PUSH1, 26, DUP1, PUSH1, 26, PUSH1, 0, CODECOPY, PUSH1, 52, JUMP, PUSH1, 0, CALLDATALOAD, PUSH1, 0, BYTE, PUSH1, 0, DUP2, EQ, ISZERO, PUSH1, 24, JUMPI, P USH1, 0, PUSH1, 64, MSTORE, PUSH1, 32, PUSH1, 64, RETURN, JUMPDEST, POP, JUMPDES T, PUSH1, 0, RETURN] $ serpent pretty_compile dont_init_zero.se | fold [PUSH1, 0, PUSH1, 32, MSTORE, PUSH1, 32, PUSH1, 32, RETURN, PUSH1, 26, DUP1, PUS H1, 21, PUSH1, 0, CODECOPY, PUSH1, 47, JUMP, PUSH1, 0, CALLDATALOAD, PUSH1, 0, B YTE, PUSH1, 0, DUP2, EQ, ISZERO, PUSH1, 24, JUMPI, PUSH1, 0, PUSH1, 64, MSTORE, PUSH1, 32, PUSH1, 64, RETURN, JUMPDEST, POP, JUMPDEST, PUSH1, 0, RETURN]
You don't actually need to follow exactly what the bytecode is doing, though it's interesting to follow all the gymnastics needed just to create a contract that doesn't do anything. Notice that the version that doesn't intialize to zero is a few intructions shorter.
I have some more contracts either written or in the brain hopper waiting to be realized, but that's all for now. Note that none of these are actually published on any blockchain yet, so these are more blueprints or first drafts than actual pieces. Still, I think the point I'm trying to make, if I'm making one at all, is that if Ethereum is a rich enough medium to allow for subsersive (I flatter myself, but I'm so humble about it) art projects, then it is likely expressive enough for a whole universe of things that haven't even been thought of yet.
There are several smart contract platforms emerging right now, but the most exciting of these is called Ethereum. Ethereum, growing in about a year from an informal whitepaper about some novel ideas in blockchain technology to a worldwide organization regularly shipping working examples of an increasingly sophisticated ecosystem for full GUI-driven distributed "Dapps" (I was unfortunately not consulted on the coining of that word). This commitment to making the whole process of using distributed trustless applications attractive for non-technical citizens is the what will lead to its success over other groups working on similar smart contract schemes or if not then something very much like it in the future. In particular, this is an advantage over projects that are focusing on using or creating blockchains for their own specific application (Namecoin attempting to replace DNS, Ripple setting up an alternate payment clearing system), on replication Bitcoin with relatively minor changes to address specific gripes about it in particular (Litecoin) or simply as a vehicle for speculation (Dogecoin), or which are building the hard crytpo blockchain-side stuff while mainly punting on UI/UX by centralizing it in a web application (Counterparty and their Counterwallet).
Whatever specific project we settle one, I do firmly believe that we are entering the Blockchain Age, built on the Information Age much as that in turn was built on the Industrial Age. My silly sarcastic programming ditties are among the first drops in a deluge of innovation.