From ead7108b986566a3ae4402d7f735578e764081f4 Mon Sep 17 00:00:00 2001 From: Tadge Dryja Date: Tue, 2 Feb 2016 01:18:59 -0800 Subject: [PATCH] add findpre and docs about nLockTime hints --- elkrem/findpre.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++ uspv/txstore.go | 1 + 2 files changed, 77 insertions(+) create mode 100644 elkrem/findpre.go diff --git a/elkrem/findpre.go b/elkrem/findpre.go new file mode 100644 index 000000000..f964ed5d9 --- /dev/null +++ b/elkrem/findpre.go @@ -0,0 +1,76 @@ +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) +} diff --git a/uspv/txstore.go b/uspv/txstore.go index 7d4f4e5a5..806966a18 100644 --- a/uspv/txstore.go +++ b/uspv/txstore.go @@ -131,6 +131,7 @@ func (t *TxStore) ExpellTx(tx *wire.MsgTx) error { for _, in := range tx.TxIn { for i, myutxo := range t.Utxos { if myutxo.Op == in.PreviousOutPoint { + hits++ loss += myutxo.Txo.Value // delete from my utxo set