Hacker News new | past | comments | ask | show | jobs | submit login
The programming error that cost Mt. Gox 2609 Bitcoins (righto.com)
172 points by peter123 on March 3, 2014 | hide | past | favorite | 55 comments



There is an alternate blockchain explicitly created for testing called the "testnet." It works exactly like Bitcoin except its genesis block is different, so it has a different blockchain and set of transactions. Testcoins have no value, and the mainstream client switches to a new testchain every so often.

Lose testcoins, not Bitcoins.


Dear software engineers working with money or crypto: please write some tests.


More importantly, if you're dealing with anything critical, start with the assumption your code if full of bugs and will fail in ways you haven't even thought of. Now make sure you have fail-safes and procedures in place outside of your code to catch these failures and deal with them as early as possible.


In some ways this is worse than writing code for something like the Therac-25: there are hostile people out there who will actively try to destroy you. If they can cause you $10 million in damages to get $10,000 for themselves they will consider that a good day.

This was the guy who wrote his own SSH server (in PHP, FWIW) and put it into production, right? This whole thing is a disaster waiting to happen.


>This was the guy who...

I missed something. Which guy?



Maybe MtGox does have tests but they wrote their own unit testing framework and it doesn't unit test correctly?

http://blog.magicaltux.net/2009/09/19/striving-for-a-better-...


Go kind of makes you do this. Last weekend I was writing some code, ran the tests, and saw that they failed. I debugged my test harness for a while, only to find that the bug was actually in my real code. (Overall I kind of like the strategy, I'd much rather debug my own for-loop-over-test-data than someone else's. But it does lead the mind down different paths than when you use something like JUnit/Hamcrest.)

Incidentally, this is why "test first" is more than just a methodology for selling high-priced consultants. At least it lets you see your tests fail and then pass, rather than just pass. Lots of common patterns pass in the presence of incorrect code.

An example that a coworker was complaining to me about recently:

  void testFooBarException() {
    try {
      thisShouldThrowFooBarException()
    } catch (FooBarException ignored) {}
  }
Can you spot the bug? The test still passes even if thisShouldThrowFooBarException doesn't throw an exception. Oops.

I personally avoid this by checking that I can make the test fail when I expect it to fail, by editing some values or commenting something out. But that doesn't scale, that only saves you once.

Something to think about.


In case anyone is wondering, that code should read something like:

  void testFooBarException() {
    try {
      thisShouldThrowFooBarException()
      fail() // Should have thrown exception
    } catch (FooBarException ignored) {}
  }
If you're feeling super fancy, you can even do some asserts in the catch block to make sure the FooBarException has an expected exception message.


I'm glad I work in a language where this is

  assertRaises(FooBarException, thisShouldThrowFooBarException)
Impossible to mistype, and states exactly what should happen.


First thing to do when working with the former is to write some code that will allow you to write the latter.


With Junit, just write

  @Test(expected = FooBarException.class)
  void testFooBarException() {
      thisShouldThrowFooBarException()
  }


PHPUnit has this in the comment annotations

    /**
     * Tests that thisShouldThrowFooBarException throws FooBarException
     *
     *  @expectedException FooBarException
     */
    public function testFooBarException() {
      thisShouldThrowFooBarException();
    }
but I much prefer

    /**
     * Tests that thisShouldThrowFooBarException throws FooBarException
     */
    public function testFooBarException() {
      $this->setExpectedException('FooBarException');
      thisShouldThrowFooBarException();
    }
Much more explicit and you're less likely to miss it when trying to grok someone else's unittest.


That's quite an old fashioned way of testing exceptions, TestNG has allowed you to do the following since 2005:

    @Test(expectedExceptions = FooBarException.class)
    public void test() {
      thisShouldThrow();
    }
If the method doesn't throw or throws the wrong kind of exception, this test will fail.


That has the problem of separating the statement that is supposed to throw and then thrown exception. E.g. a slightly more complicated test case:

    @Test(expectedExceptions = FooException.class)
    public void test() {
      firstDoThis();
      thenThat();
      thenSomeMore();
      thisShouldThrow();
    }
Now you're only asserting that any of these statements throw, anywhere in the code under test. That's significantly weaker and I've seen it mask real problems in code.

I think using plain try something/fail/catch is clearer, or using Closures and an assertThrows if your language supports it.


You should probably think of splitting up your test case if it's this complicated.


You can always revert to the old try/catch/fail if you need to test something more complex (e.g. testing the message of the exception) but most of exception tests fall in the simple case shown above.


In that case, you first write a test case ensuring that the first three statements won't throw.


JUnit4 kind of helps with ExpectedExceptions. try-catch doesn't usually have a good reason to exist in unit tests.


> avoid this by checking that I can make the test fail when I expect it to fail, by editing some values or commenting something out.

Whenever I do this, I feel awful, because:

> But that doesn't scale, that only saves you once

You need to fuzz test your tests. (I always forget the name of the awesome Java tool for this....) If you can randomly mutate your test code (negate boolean conditional, etc) and your tests still pass, they fail.


I often deliberately break something and make sure it fails before accepting that my test actually tests for what I think it does. Not perfect, but it gets you some of the way there.


Check out a thing called "mutation testing" - it changes your code and checks for test failures. If no tests fail, the mutation test fails.


oh really? you edit your some values or comment something to make sure that your tests fail on failure?

shouldn't you be automating that? :-D


MtGox: the apotheosis of Not-Invented-Here.

EDIT: NIH is pretty bad overall, but it's especially terrible in crypto, where 'useless' and 'useful' take on something akin to binary values, rather than a continuum.


Remember, when working with bitcoin, that transactions are _irreversible_. Even in the real billion-dollar finance world transactions have been reversed for serious software errors. Not in bitcoin.


Dear software engineers working with money or crypto: hire security consultants. Pay them with your favorite cryptocurrency.



And then test them live on the testnet. That's what it's there for.


The more that I am wrapping my head around it, the more I realize this all may not be an accident.

My conspiracy theories side tells me this: imagine that you own a bank that is not guarded by any rules laws or regulations, that most money is deposited anonymously or semi-anonymously, and it is deposited in unmarked banknotes. And its worth $500k.

Now, fast forward to today and you keep keys to $400,000,000 vault. Only rules haven't changed. Now, given the overall complexity of bitcoins algorithm, the fact that mt gox is (afaik) not even registered in a country with solid laws, wouldn't you grab the money and "run", even though running means only pointing out there was a glitch in the system and filing for bankruptcy protection?

If any of this is true, then here is your (sad) reasoning why we need government oversight when it comes to dealing with someones property, being it even bits on someones hard drive.


How is Japan not "a country with solid laws".


I read it as saying that Japan was not a country with solid laws that relate to Bitcoin. One requirement for having solid laws, would be some sort of compulsory security audit of the code by independent experts. Japanese law did not require this, so did not have solid laws relating to Bitcoin exchanges.


> One requirement for having solid laws, would be some sort of compulsory security audit of the code by independent experts.

This doesn't exist in the U.S. financial system. Maybe it should, but honestly it is such a stronger requirement than anything close to what we have currently, and I imagine it would be impossible to enact on a resistant market (try defining "independent security experts"... oy).


"solid" being compared with the holy crap of regulations the US have.

I (a German) laugh my behind off when I read that as a mail-order business one is expected (and fined if noncompliant to the letter) to the tax codes of villages so small that they would not even count as a village in Germany. And if you're shipping inside the European Union, well, just properly declare the VAT and be done with it.


As a German, you may want to brush up on your understanding of U.S. law. First, if you're a mail order business in California you don't have to pay attention to any other state's sales or use tax laws unless you have a business presence (such as an office) in that state. As a California shipper shipping in-state you need to look up the destination zip code's tax rate, true, but it's county-by-county and you can do it for free here: https://maps.gis.ca.gov/boe/TaxRates/

Second, if you want to avoid even this annoyance, you can start your business in one of the five states with no sales taxes including New Hampshire or Oregon, meaning you never have to collect any sales taxes. Third, if you do have offices in other states, you can buy some relatively cheap software that takes care of billing for you.

I'm not saying this system is perfect or even that it'll continue given the pro-Internet tax forces in Congress, but nevertheless I don't think your description is accurate.


Yelling "government" when we can't find an obvious solution to a complex problem is a childish habit that everyone seriously needs to break.

In this case there is even plenty of evidence that it won't work: Government regulated banks have simply discovered less obvious means to run off with the plunder.

What we really need is strong stance from the bitcoin foundation against any service that offers to hold user's wallet keys.


I read the link on proof-of-burn, which demonstrated how it works, but I'm confused. What is the actual point of doing a proof-of-burn?


If I want to send an email to a political party I don't like, they may want me to sacrifice money to show I'm not a spammer. I don't want to send them money, so we agree to burn some instead.


Interesting, although that seems like a fairly niche case. Are there other more generic applications? The post also linked to something called Counterparty, but the front page of that site gave no hint as to how proof-of-burn is used there.

I'm also curious as to what's stopping you from using the same proof-of-burn repeatedly. If I burn some coins 2 months ago, and then prove to someone today that I burned then, why can't I turn around and use the exact same burn as a proof for someone else tomorrow?


Generally, any situation where a charge is intended to reduce demand / disadvantage the payer, rather than to maximise profit / benefit the payee.

At the moment in these sorts of cases usually a payee just keeps the money; the advantage would be in reducing perverse incentives.

If spammers kept opening HN accounts, HN could say "burn $5 to open a new account" to reduce demand. That would avoid HN having an incentive to ban people for profit.

Or the state could say "speeding fine? burn $200" to discourage people from speeding. That would avoid the state having an incentive to increase crime to generate fine revenue.

Or a college could say "want your exam paper re-graded? burn $50" to discourage requests just for the sake of it. That would avoid the college having an incentive to grade people badly.

To resolve a disputed transaction on an auction site, the site could require the buyer to destroy the goods and the seller to burn the money, so there would be no profit from filing false disputes.


Hrm, the ability to do this is actually rather intriguing.

I wonder how feasible it would be to have a service that provides proof-of-burn for real-world money? Effectively, allow people to pay the company money, company then provides burner with a receipt referencing this proof-of-burn. The burner can then provide that receipt to the party that wishes the burn, and the party can verify with this service that the money was indeed burnt (and this verification then marks the burn as having been "used", so to speak). The company could do something like take a small fee from the burnt money (to actually pay for the service), and donate the rest to charity (and maybe even provide the donation receipt to the burner, so they can treat it as a charitable gift for tax purposes).

The big obstacle here is how to ensure that the money is irrevocably burnt, but that's a solvable problem (check or wire transfer, perhaps, or even waiting long enough after a credit card payment to prevent chargeback, although I think that's a rather long time).

The downside here is that you need a middleman to run the whole operation, and irrevocably paying them takes a bit of effort and time.

The upside is the money isn't actually destroyed, it's instead given to charity.



I keep wondering though if this can be thought of as a variation of the halting problem. I.e. it is a question of halting state rather than halting execution. The problem here seems to be the inverse:

Can you guarantee that, over time, the program sequence will not halt even if the individual programs in fact do halt.

"Halting" would have to be defined differently, i.e. with the capacity to resume.

Am I missing something?


Arguably since the transaction scripts don't loop, something resembling 100% branch coverage testing would probably suffice. The issue is classifying the test results: should the test succeed with these inputs, should it fail with these other inputs, etc.

Given that the scripts are usually used to verify basic transactions, though, you could probably provide a few basic rules and autogenerate appropriate tests.


Arguably a mining approach that would require that miners return the transactions that can't complete to their previous individual could provide formal proof that it will not "halt" ever.

It seems to me that the obvious issue is sending invalid transactions. If these are not handled and the bitcoins are lost that means that bitcoin itself must eventually disappear because some percentage of transactions will be bad and over time, the sum of all of these will lead to enough losses to ensure that there is no practical value to the remaining bitcoins.

It seems to me that programs can be guaranteed not to halt a lot more than they can be guaranteed to halt.


What we really need is a strong stance from the bitcoin foundation condemning any services that offers to hold user's wallet keys for them.


This is not the only or even the primary problem with MtGox. There are fundamental control shortcomings, among various tech problems.


is it alarming at all that the # of redeemable bitcoins are provenly monotonically decreasing after some period of time? I guess if there is a lower limit it shouldn't matter?


It's not really that alarming. It has some deflationary effects (which Bitcoin was designed with anyway). Worst case, let's say all Bitcoins except .00000001 (1 Satoshi) have been destroyed. Well, no problem, we just make the base unit .00000000000000000001 Bitcoin or something.

As far as I know, the only reason 21,000,000*100,000,000 units was chosen was to fit comfortably in the 52-bit mantissa of a 64-bit IEEE 754 floating point number.

Edit: Apparently I just made that up... But it makes sense! Edit 2: Apparently some other devs believe this is the case as well.


BTC use fixed-point math with 64-bit unsigned integers. Precision is 8 decimal places. So at least as currently built, I believe 1 Satoshi is the smallest possible unit of Bitcoin.

There's talk about it being possible to move the decimal point with a protocol update, but I'm not sure about how the logistics of that would work, or just how far it's possible to move it.


How can a protocol dealing with currency invented after year 2000 have limits on the number of decimals or its positionn?


In a nutshell, because sometimes the point really does not (and should not) float.

The main reason for choosing variable-precision numbers for general-purpose computing is that they have a very wide dynamic range that can adjust automatically, which makes them easier to use in more situations. In financial applications, that advantage doesn't really apply; banks don't often encounter situations where customers are worried about a ten thousandth of a cent, and they never encounter situations where it's suddenly OK to only track a transaction with dollar-level precision.

Meanwhile there are some risks to using arbitrary-precision math too. If BTC used floats, then that might result in it being vulnerable to attacks that are able to take advantage of rounding errors in floating-point math. If BTC used infinite-precision decimals, then that might result in it being vulnerable to attacks that work by creating ridiculously high-precision numbers that implementations have a hard time dealing with.


I have 1 bitcoin. I send you 2^-32 btc. How much do I have left?


I'll hazard a guess: the protocol might not want to allow arbitrarily large transaction messages. Could introduce points of failure / DOS'ing.


The idea would have been to allow BTC software to be written in languages that only have float support (like JS).

The Bitcoin code itself actually frequently uses decimal64 as well as uint64_t.


Do people often use floats in their bitcoin code?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: