Choosing a type for blockchain height (beware of unsigned integers)

This article was originally published on Medium.

Before this pull request, it was a mess. ABCI was using a uint64 height on EndBlock. Tendermint Core was using the same type for TxResult. Block and BlockStore, however, were using int heights (remember that int is different depending on the processor architecture: int32 for x86, int64 for amd64). There was no single standard across our repositories as for what type to use for blockchain height.

There was a need [1]. If you have different types everywhere, you need to cast them, which can lead to information loss:

Users should be able to build upon Tendermint without worrying that some type is going to change overnight.

So one day we sat down and decided to do a small research. Choosing between uint64 and int64 was not an obvious choice. At least not back then. Blockchain height should only go up, so it is logical to suggest uint64. And with a maximum value being 18446744073709551615, a chain can live up to 584 million years (assuming one sec. blocks). Wow. That’s life! What about other projects? Parity and chain use uint64. Sounds like an obvious choice? But if you look deep enough, there are dragons.

Ruling out int type

int was ruled out almost immediately due to size (32 bits on x86) and inconsistency between nodes with different architectures.

Assuming a one second block time this means on a 32-bit architecture a Tendermint chain can only live 68 years, which is just under the worldwide mean life expectancy for a male. Surely a Tendermint chain should live longer!

uint64 vs int64

Benefits of uint64

Downsides of uint64

I am just saying that if we decrement an uninitialized height uint64(0), all of a sudden that’s a quick mess up. if we have code that can check if height <= 0 then we are set with int64. but with uint64 only code Jesus can help us

package main
import (
    "fmt"
)
func main() {
    var a int64
    a = 10
    a -= 29
    if a < 0 {
        fmt.Println("panic")
    }
    fmt.Printf("a=%v\n", a)
    var b uint64
    b = 10
    if (29 - b) > 10 {
        fmt.Println("underflow")
    }
    b -= 29
    fmt.Printf("b=%v\n", b)
}

https://play.golang.org/p/YvQgJ6Bm2Xu

Note on uint64 and Java

Java does not support unsigned ints. It means ABCI apps, that are written in Java won’t be able to go beyond int64 anyway. If you are curious about why read Why doesn’t Java support unsigned ints?

Benefits of int64

Downsides of int64

In conclusion

Given above reasoning we decided to go with int64. “value should never be less than zero” should not be a primary reason for picking uint64 as a type for blockchain height. If you are gonna use uint64, be extra careful with arithmetic.

Resources:

Comments

comments powered by Disqus