Making a Rainbow

Now that we know about Hunks, and most importantly, the raindrop/evaporate functions, we can actually build some Rainbows.

When a proof is found, the proof must come with some parameters used to create the Rainbow. Without the parameters, the proof is invalid (in fact, it can't be validated at all!)

These parameters are:

#![allow(unused)]
fn main() {
pub struct RainbowParams {
    /// A unique, random value.
    rainbow_id: Hunk,

    /// Your public key.
    miner: Hunk,

    /// The developers public key.
    dev: Hunk,

    /// dev_fee/256 * block rewards goes to developer.  You get the rest.
    dev_fee: u8,

    /// The compression level, 1-32.
    z: u8,
    ...
}

Note: You'll notice a dev/dev_fee parameter. The reference implemnation will not require a dev fee for rainbow tables. However, I want to make sure other developers can cleanly implement a dev fee. Dev fees are very common in the crypto world, and it would be nice if they weren't random, and both the dev and miner got what they agreed upon in each found block. By putting the fee here, it guarantees that this Rainbow will always use this dev fee structure.

However, in addition to those parameters, we must also compute a value called fast_hash. This is technically not required, but speeds up generating the rainbow table significantly.

#![allow(unused)]
fn main() {
    ...
    fast_hash: Hunk,
}

impl RainbowParams {
    /// Combine all the parameters into one hash.
    fn compute_fast_hash(&mut self) {
        self.fast_hash = Hunk::combine(
            Hunk::combine(
                self.rainbow_id,
                self.miner,
            ),
            Hunk::combine(
                self.dev.combine_hash(self.dev_fee),
                Hunk::hash(self.z as u32),
            )
        )
    }

    /// Use the raindrop function of fast_hash as our hashing
    /// algorithm for _this_ rainbow table.
    fn raindrop(&self, value: u32) -> Hunk {
        self.fast_hash.raindrop(value)
    }
}
}

The Rainbow Chain (not the chain in Blockchain)

We will first create a bunch of chains. A chain is a starting value: start: u32, and a final value, end: u32.

We will start with a bunch of arbitrary numbers for start. To find end, we will take the raindrop of start (which will give us a Hunk), evaporate that Hunk (to get a u32), then take the raindrop of that u32 ... and so on, calling raindrop then evaporate then raindrop ... until the u32 part matches the terminating condition.

The terminating condition will be, when the first self.z bits of u32 are all 0s, that u32 is the end.

#![allow(unused)]
fn main() {
impl RainbowParams {
    pub fn terminator(&self, value: u32) -> bool {
        value.leading_zeros() >= self.z
    }
}
}

This computation will look like the following:

start -> a = raindrop(start) -> b = evaporate(a) => c = raindrop(b) -> d = evaporate(c) -> e = raindrop(d) -> f = evaporate(e)

until the final value has self.z 0's at the start. We will then throw out all the middle-bits, because we can regenerate them at any time with just the start value.

Saving the rainbow for later

We will keep a separate sqlite database for the RainbowParams structure. Each RainbowParams will be given a sequential, incrementing id starting at 0. This is important for some space-saving tricks.

Once the RainbowParams are saved, and the ID is correct, all we actually need to do is append the start value in a file called end.

The index in the file will bring us back to the current RainbowParams, and the end is the key we want to look up by.

This end file will now contain a start value for all rainbow tables. When we read from it, we will get many results, which is many chances to win! And, that read is now sequential, rather than random, so we can read even more!

If a particular Rainbow does not have an end, we place a dummy value in the file. We can filter it out later. It doesn't matter what the dummy value is.