5

Consider any given block of the chain (e.g. this one). Its hash code is:

BL7wTUBp1apNisdzsjbb8dRYGGxoKDQxeMB3XqBNHoiqeL5URs2

I am developing a tool that takes this hash code and produces a "random" number out of it. To achieve my goal, I need to understand:

  1. What is the algorithm used to produce this code?
  2. How "random" are the letters and numbers in this code?

Any help is more than welcome.

1
  • 1
    One test you can do is first assess the distribution properties of base58 checksum by generating a truly random set of payload bytes and passing this to base58 checksum and then do the transformation you suggest. Then perform a KS test to assess if the distribution of digits is statistically the same as the experimental one.
    – Ezy
    May 17, 2019 at 1:25

2 Answers 2

7
+100
  1. Take a byte representation of a particular block header: http://rpc.tzkt.io/mainnet/chains/main/blocks/head/header/raw. The format of the block header is described in the docs.
  2. Get a BLAKE2b (32bit) hash digest of it
  3. Prepend two bytes '\x01\x34' (they are responsible for "B" letter)
  4. Base58 encode it with checksum

There is no randomness.

The baker chooses the block header. A baker can easily construct many different blocks and inject only the block with the most desired hash, manipulating your 'random' number generation. Because a Tezos block only requires an easy proof of work, this is easier than in Bitcoin or Ethereum, where the advice "no, you can't generate random numbers that way" has been given for years.

7
  • Thanks. So the next question is, how is that block header selected? Where is the original source of "pseudo-randomness" coming from?
    – luchonacho
    May 10, 2019 at 13:21
  • How is the block header produced? What makes one block header different from the other?
    – luchonacho
    May 11, 2019 at 8:34
  • 1
    If you feel you need to ask followup questions, please edit your original question (and title?) to be more clear. This seems to answer the question you wrote so far. The answer is very credible on its own without "official" sources. Just try it and see that you do indeed get the expected hash. (You do.)
    – Tom
    May 13, 2019 at 20:27
  • @Tom I realized the original question didn't suffice for my purposes. Hence the follow up one. I updated the post. Also, albeit the proceedure works, it would be interesting to know where it comes from. E.g. is this written in the blockchain?
    – luchonacho
    May 14, 2019 at 7:48
  • I tried to adapt the classic warning to Tezos. I'm not sure whether there are any useful citations about the block hash. In any case the details won't change the answer to your real question: don't.
    – Tom
    May 14, 2019 at 14:26
1

Part 1 of my question was answered by Michael. What about the second part? Well, I just had a look at it. What I did is:

  • extract the full series of hash codes, from block 1 to the latest (VERY LONG).
  • extract the numbers contained in such hash codes (i.e. remove letters).
  • analyse the "randomness" of this series of numbers with purposely designed tests.

The tests suggest that the randomness hypothesis cannot be rejected at the 5%. Interestingly, the distribution of numbers is very skewed. If anything, one might expect it to be somewhat resembling a uniform distribution (noticing that the contrary is not true, i.e. uniform distribution does not imply randomness). I'm still pounding on this, but a first look suggest that the series might be random.

The R code to reproduce the above analysis is below:

# Initialise stuff

remove(list = ls())    
options(timeout = 1000000) # in case request times-out

library(jsonlite)
library(ggplot2)
library(randtests)

# Get maximum number of blocks

last_block <- fromJSON("https://api6.tzscan.io//v3/head")
N <- last_block$level
blocks <- seq(1,N,by = 1)
hashs <- vector(mode="character", length = N)

# Download all hash codes (timer and print included, for analysis) VERY LONG

start <- proc.time()
for (i in 1:N) {
  url <- paste0("https://api6.tzscan.io//v3/block_hash_level/",i)
  hashs[i] <- fromJSON(url)
  print(i)
}
finish <- proc.time() - start

# Remove letters

n_hashs <- as.numeric(gsub("\\D+","", hashs, perl = TRUE))
df <- data.frame(value=n_hashs)
names(df)[names(df) == "df.value....." ] <- "value"

# Plot (definitively not a uniform distribution, for any level of zooming in)

ggplot(df, aes(x=value)) + geom_histogram() 

# Randomness tests (indicating the number sequence is not random)

bartels.rank.test(df$value)
cox.stuart.test(df$value)
difference.sign.test(df$value)
rank.test(df$value)
runs.test(df$value)
turning.point.test(df$value)
3
  • 1
    One expects the hashes to look uniformly random. The vanilla baker will generate pseudorandom POW nonces until the POW succeeds. Thus the chosen hashes should look uniformly random. I suggest generating (bogus) block hashes, choosing each bit uniformly at random and then converting to base58, and plugging them into your analysis. In any case, note that bakers have no reason to manipulate the hash today, but could easily do so if you give them a reason.
    – Tom
    May 16, 2019 at 16:49
  • Side note: it is not necessary to spam tzscan. You can get lots of hashes in one go using the node RPC /chains/main/blocks/ with the length and head query parameters.
    – Tom
    May 16, 2019 at 16:51
  • @Tom Thanks for the suggestions. Regarding the last one, is there a manual on how to use the node RPC?
    – luchonacho
    May 17, 2019 at 8:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.