Hacker News new | ask | show | jobs
by scientaster 3337 days ago
Would have been much easier with an ethereum smart contract. No gimmicky address list, it's a crowdsourced picture in the blockchain.

  contract Place {
    struct Pixel {
      bytes3 color;
      uint lastPayment;
    }

    Pixel[10000][10000] board;

    modifier onBoard(uint x, uint y) {
      if (x > 10000 || y > 10000) {
        throw;
      } 
      _;
    }

    // Accepts funds, if the funds sent are greater than funds for that pixel last, change the color.
    function colorPixel(uint x, uint y, bytes3 color) payable onBoard(x, y) { 
      Pixel p = board[x][y];
      if (msg.value > p.lastPayment) {
        msg.sender.send(p.lastPayment); // refund previous price of pixel
        p.color = color;                // set the color
        p.lastPayment = msg.value;      // set new cost to whatever the person sent in
      } else {
        throw;
      }
    }
  }
May contain an error or two, but that's the general gist of it.
3 comments

> msg.sender.send(p.lastPayment); // refund previous price of pixel

This code suffers from Recursive calling vulnerability (same bug which caused the DAO to be hacked).

You're sending the money BEFORE you're updating the balances.

There is a better way to write the payment code (and also wouldn't take you more than 5 minutes to write).

Here is one way to do it:

    function colorPixel(uint x, uint y, bytes3 color) payable onBoard(x, y) { 
      Pixel p = board[x][y];
      lastPayment = p.lastPayment;
      p.lastPayment = msg.value;
      if (msg.value > lastPayment) {
        if(!msg.sender.send(lastPayment)) throw; // refund previous price of pixel
        p.color = color;                // set the color
      } else {
        throw;
      }
    }
Another shorter way:

    function colorPixel(uint x, uint y, bytes3 color) payable onBoard(x, y) { 
      Pixel p = board[x][y];
      require(msg.value > p.lastPayment);
      if(!msg.sender.send(p.lastPayment)) throw; // refund previous price of pixel
      p.color = color;                // set the color
      p.lastPayment = msg.value;      // set new cost to whatever the person sent in
    }
Good analysis of the code, and nice correction. Agreed that you should always check your sends' return values, too! In this example you have to send more than the last guy, so I think a recursive call would still put in more ether than would be withdrawn. Still undesired though.

If it were fleshed out more it should probably return the ether to the last depositor instead of the purchaser.

Thanks for the feedback!; I'm tempted to turn this into an example Dapp later today.

Is this code equivalent to the current code?

IUUC the current code accumulate the payment to each pixel and whatever color has more accumulated money is chosen.

IIUC your code only consider the last payment and to replace the old color you have to outbid it, without any consideration of the previous payments.

Another strategies:

Penny auction: Whatever color received the last payment is the chosen one. This is more similar to /r/place

Generals.io: If someone adds money to the current color, then increase the accumulated money in that color. If someone adds money to another color the amount is discounted. Unless the result is negative and the color changes.

It's not equivalent to what OP made, the logic is slightly different. It took 5 minutes and it was just to demonstrate how easy it would be to make something similar. If you wanted to mirror OP you could build out the pixel struct to have a color to value mapping.

The penny auction one is even easier:

  bytes3[10000][10000] board;
  function colorPixel(uint x, uint y, bytes3 color) onBoard(x, y) { 
    board[x][y] = color;
  }
Users would only have to pay the gas cost of using the ethereum network, the contract wouldn't hold any funds itself. Again, it's pretty much whatever you make out of it. You're not really constrained when it comes to building in logic into these contracts, anything (logically) you can do with any other languages is possible here.
Guess I know what I am playing with soon :)