lnd.xprv/elkrem/findpre.go

77 lines
2.9 KiB
Go
Raw Normal View History

package elkrem
import (
"bytes"
"fmt"
"github.com/btcsuite/btcutil"
)
/* findpre - find the pre-image for a given hash
The worst (?) has happened and your channel counterparty has broadcast an old,
invalid state. That's bad. But what, that means you get to take all the money.
This is the fun part of the channel enforcement mechanism.
The old transaction they broadcast has an nLockTime field which provides a hint
about which state number, and which hash index, was used. Bitcoin's nLockTime
field counts as blocks from 0 to 499,999,999 (which would happen somtime after
10,000 CE) and as a unix time from 500,000,000 up to 4,294,967,296 (which is
much sooner, in 2106). There is some extra space we can use here in both cases,
though the unix time gives us much more. nLockTimes from 500,000,000 to
1,036,870,912 are safe, representing dates from 1985 to 2002, which are before
any possible bitcoin transaction. 1,036,870,912 is 500,000,000 plus
536,870,912, and 536,870,912 is 2^29, so we have 29 bits of free space.
After subtracting 500,000,000, the remaning lowest 29 bits are the bits of the
index within the elkrem receiver used in that transaction. That way, even with
a trillion previous channel states (2^40) we will only need to search through
2048 possible branches to find the right pre-image. In most cases, there will
be fewer than 536,870,912 previous states and we can seek directly to the
correct pre-image.
*/
func (e *ElkremReceiver) FindPre(
target [20]byte, timeHint uint32) (*[20]byte, error) {
maxUint32 := uint32((1 << 32) - 1)
minTime := uint32(500000000)
hintRange := uint32((1 << 29) - 1)
// a timeHint of 2^32 (4294967296) means we don't have a timeHint.
if timeHint == maxUint32 {
return nil, fmt.Errorf("no timeHint")
}
// valid timeHint range is 500M to 500M + 2^29
if timeHint < minTime || timeHint > minTime+hintRange {
return nil, fmt.Errorf("timeHint %d out of range (500M - ~1G)", timeHint)
}
indexHint := uint64(timeHint - minTime)
maxIndex := e.s[len(e.s)-1].i // highest index we have
if indexHint > maxIndex { // we can't derive needed index
return nil, fmt.Errorf("hint index %d greater than max index %d",
indexHint, maxIndex)
}
// iterate though, adding 2^29 each time.
// there is some redundancy here when you have a large number of guesses
// to go through, so this could be optimized later.
for guess := indexHint; guess < maxIndex; guess += uint64(hintRange) {
sha, err := e.AtIndex(guess) // generate preimage
if err != nil {
return nil, err
}
var truncatedSha [20]byte
copy(truncatedSha[:], sha.Bytes()) // copy into 20 byte array
checkHash := btcutil.Hash160(truncatedSha[:]) // hash and compare
if bytes.Equal(target[:], checkHash) { // matches hash, return
return &truncatedSha, nil
}
}
// got through the loop without finding anything.
return nil, fmt.Errorf("Couldn't find preimage of %x. timeHint %d bad?",
target, timeHint)
}