From 0d3639435f0edb503b651db0f126e90ff9f3db8f Mon Sep 17 00:00:00 2001 From: Tadge Dryja Date: Mon, 15 Feb 2016 22:13:17 -0800 Subject: [PATCH] can sync with segnet in hard mode lots of changes but they seem to work --- lnwallet/channel.go | 2 +- lnwallet/wallet.go | 4 +- lnwire/lnwire.go | 11 ++-- shell.go | 2 +- uspv/hardmode.go | 133 ++++++++++++++++++++++++++++++++++++-------- uspv/msghandler.go | 13 ++++- uspv/txstore.go | 10 ++-- 7 files changed, 135 insertions(+), 40 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 93ad4938..8bb4fa62 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -86,7 +86,7 @@ func newLightningChannel(wallet *LightningWallet, events chainntnfs.ChainNotifie return nil, err } _, multiSigIndex := findScriptOutputIndex(state.FundingTx, fundingPkScript) - lc.fundingTxIn = wire.NewTxIn(wire.NewOutPoint(&fundingTxId, multiSigIndex), nil) + lc.fundingTxIn = wire.NewTxIn(wire.NewOutPoint(&fundingTxId, multiSigIndex), nil, nil) lc.fundingP2SH = fundingPkScript return lc, nil diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 372b6cf3..8aaed0ac 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -529,7 +529,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // Empty sig script, we'll actually sign if this reservation is // queued up to be completed (the other side accepts). outPoint := wire.NewOutPoint(coin.Hash(), coin.Index()) - ourContribution.Inputs[i] = wire.NewTxIn(outPoint, nil) + ourContribution.Inputs[i] = wire.NewTxIn(outPoint, nil, nil) } l.coinSelectMtx.Unlock() @@ -794,7 +794,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // since the outputs are cannonically sorted. fundingNTxid := fundingTx.TxSha() // NOTE: assumes testnet-L _, multiSigIndex := findScriptOutputIndex(fundingTx, multiSigOut.PkScript) - fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingNTxid, multiSigIndex), nil) + fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingNTxid, multiSigIndex), nil, nil) // With the funding tx complete, create both commitment transactions. initialBalance := ourContribution.FundingAmount diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index c9fdb962..bf07419d 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/binary" "fmt" + "io" + "io/ioutil" + "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "io" - "io/ioutil" ) var MAX_SLICE_LENGTH = 65535 @@ -21,12 +22,12 @@ type CommitHeight uint64 // Subsatoshi amount (Micro-Satoshi, 1/1000th) // Should be a signed int to account for negative fees -// +// // "In any science-fiction movie, anywhere in the galaxy, currency is referred // to as 'credits.'" // --Sam Humphries. Ebert, Roger (1999). Ebert's bigger little movie // glossary. Andrews McMeel. p. 172. -// +// // https:// en.wikipedia.org/wiki/List_of_fictional_currencies // https:// en.wikipedia.org/wiki/Fictional_currency#Trends_in_the_use_of_fictional_currencies // http:// tvtropes.org/pmwiki/pmwiki.php/Main/WeWillSpendCreditsInTheFuture @@ -557,7 +558,7 @@ func readElement(r io.Reader, element interface{}) error { var txins []*wire.TxIn for i := uint8(0); i < numScripts; i++ { outpoint := new(wire.OutPoint) - txin := wire.NewTxIn(outpoint, nil) + txin := wire.NewTxIn(outpoint, nil, nil) err = readElement(r, &txin) if err != nil { return err diff --git a/shell.go b/shell.go index 365b0ac2..bc255bb7 100644 --- a/shell.go +++ b/shell.go @@ -314,7 +314,7 @@ func SendCoins(s uspv.SPVCon, adr btcutil.Address, sendAmt int64) error { return err } // make new input from this utxo - thisInput := wire.NewTxIn(&utxo.Op, prevPKscript) + thisInput := wire.NewTxIn(&utxo.Op, prevPKscript, nil) tx.AddTxIn(thisInput) nokori -= utxo.Value if nokori < -10000 { // minimum overage / fee is 1K now diff --git a/uspv/hardmode.go b/uspv/hardmode.go index 3e3bcdba..efa5a6aa 100644 --- a/uspv/hardmode.go +++ b/uspv/hardmode.go @@ -9,41 +9,128 @@ import ( "github.com/btcsuite/btcd/wire" ) -// BlockRootOK checks that all the txs in the block match the merkle root. -// Only checks merkle root; it doesn't look at txs themselves. -func BlockRootOK(blk wire.MsgBlock) bool { - var shas []*wire.ShaHash - for _, tx := range blk.Transactions { // make slice of txids - nSha := tx.TxSha() - shas = append(shas, &nSha) +var ( + WitMagicBytes = []byte{0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed} +) + +// BlockRootOK checks for block self-consistency. +// If the block has no wintess txs, and no coinbase witness commitment, +// it only checks the tx merkle root. If either a witness commitment or +// any witnesses are detected, it also checks that as well. +// Returns false if anything goes wrong, true if everything is fine. +func BlockOK(blk wire.MsgBlock) bool { + var txids, wtxids []*wire.ShaHash // txids and wtxids + // witMode true if any tx has a wintess OR coinbase has wit commit + var witMode bool + + for _, tx := range blk.Transactions { // make slice of (w)/txids + txid := tx.TxSha() + wtxid := tx.WTxSha() + if !witMode && !txid.IsEqual(&wtxid) { + witMode = true + } + txids = append(txids, &txid) + wtxids = append(wtxids, &wtxid) } - neededLen := int(nextPowerOfTwo(uint32(len(shas)))) // kindof ugly - for len(shas) < neededLen { - shas = append(shas, nil) // pad out tx slice to get the full tree base + + var commitBytes []byte + // try to extract coinbase witness commitment (even if !witMode) + cb := blk.Transactions[0] // get coinbase tx + for i := len(cb.TxOut) - 1; i >= 0; i-- { // start at the last txout + if bytes.HasPrefix(cb.TxOut[i].PkScript, WitMagicBytes) && + len(cb.TxOut[i].PkScript) > 37 { + // 38 bytes or more, and starts with WitMagicBytes is a hit + commitBytes = cb.TxOut[i].PkScript[6:38] + witMode = true // it there is a wit commit it must be valid + } } - for len(shas) > 1 { // calculate merkle root. Terse, eh? - shas = append(shas[2:], MakeMerkleParent(shas[0], shas[1])) - } // auto recognizes coinbase-only blocks - fmt.Printf("MRs calcd %s given %s\n", - shas[0].String(), blk.Header.MerkleRoot.String()) - return blk.Header.MerkleRoot.IsEqual(shas[0]) + + if witMode { // witmode, so check witness tree + // first find ways witMode can be disqualified + if len(commitBytes) != 32 { + // witness in block but didn't find a wintess commitment; fail + log.Printf("block %s has witness but no witcommit", + blk.BlockSha().String()) + return false + } + if len(cb.TxIn) != 1 { + log.Printf("block %s coinbase tx has %d txins (must be 1)", + blk.BlockSha().String(), len(cb.TxIn)) + return false + } + if len(cb.TxIn[0].Witness) != 1 { + log.Printf("block %s coinbase has %d witnesses (must be 1)", + blk.BlockSha().String(), len(cb.TxIn[0].Witness)) + return false + } + + if len(cb.TxIn[0].Witness[0]) != 32 { + log.Printf("block %s coinbase has %d byte witness nonce (not 32)", + blk.BlockSha().String(), len(cb.TxIn[0].Witness[0])) + return false + } + // witness nonce is the cb's witness, subject to above constraints + witNonce, err := wire.NewShaHash(cb.TxIn[0].Witness[0]) + if err != nil { + log.Printf("Witness nonce error: %s", err.Error()) + return false // not sure why that'd happen but fail + } + + var empty [32]byte + wtxids[0].SetBytes(empty[:]) // coinbase wtxid is 0x00...00 + + // witness root calculated from wtixds + witRoot := calcRoot(wtxids) + + calcWitCommit := wire.DoubleSha256SH( + append(witRoot.Bytes(), witNonce.Bytes()...)) + + // witness root given in coinbase op_return + givenWitCommit, err := wire.NewShaHash(commitBytes) + if err != nil { + log.Printf("Witness root error: %s", err.Error()) + return false // not sure why that'd happen but fail + } + // they should be the same. If not, fail. + if !calcWitCommit.IsEqual(givenWitCommit) { + log.Printf("Block %s witRoot error: calc %s given %s", + blk.BlockSha().String(), + calcWitCommit.String(), givenWitCommit.String()) + return false + } + } + + // got through witMode check so that should be OK; + // check regular txid merkleroot. Which is, like, trivial. + return blk.Header.MerkleRoot.IsEqual(calcRoot(txids)) +} + +// calcRoot calculates the merkle root of a slice of hashes. +func calcRoot(hashes []*wire.ShaHash) *wire.ShaHash { + for len(hashes) < int(nextPowerOfTwo(uint32(len(hashes)))) { + hashes = append(hashes, nil) // pad out hash slice to get the full base + } + for len(hashes) > 1 { // calculate merkle root. Terse, eh? + hashes = append(hashes[2:], MakeMerkleParent(hashes[0], hashes[1])) + } + return hashes[0] } // IngestBlock is like IngestMerkleBlock but aralphic // different enough that it's better to have 2 separate functions func (s *SPVCon) IngestBlock(m *wire.MsgBlock) { var err error - var buf bytes.Buffer - m.SerializeWitness(&buf) - fmt.Printf("block hex %x\n", buf.Bytes()) - for i, tx := range m.Transactions { + // var buf bytes.Buffer + // m.SerializeWitness(&buf) + // fmt.Printf("block hex %x\n", buf.Bytes()) + for _, tx := range m.Transactions { // if i > 0 { fmt.Printf("wtxid: %s\n", tx.WTxSha()) fmt.Printf(" txid: %s\n", tx.TxSha()) - fmt.Printf("%d %s", i, TxToString(tx)) + // fmt.Printf("%d %s", i, TxToString(tx)) // } } - ok := BlockRootOK(*m) // check block self-consistency + ok := BlockOK(*m) // check block self-consistency if !ok { fmt.Printf("block %s not OK!!11\n", m.BlockSha().String()) return @@ -71,6 +158,7 @@ func (s *SPVCon) IngestBlock(m *wire.MsgBlock) { wg.Add(len(m.Transactions)) for i, tx := range m.Transactions { go func() { + defer wg.Done() hits, err := s.TS.Ingest(tx, hah.height) if err != nil { log.Printf("Incoming Tx error: %s\n", err.Error()) @@ -80,7 +168,6 @@ func (s *SPVCon) IngestBlock(m *wire.MsgBlock) { log.Printf("block %d tx %d %s ingested and matches %d utxo/adrs.", hah.height, i, tx.TxSha().String(), hits) } - wg.Done() }() } wg.Wait() diff --git a/uspv/msghandler.go b/uspv/msghandler.go index c3def1fb..c1ddf99f 100644 --- a/uspv/msghandler.go +++ b/uspv/msghandler.go @@ -88,10 +88,19 @@ func (s *SPVCon) fPositiveHandler() { // send filter s.SendFilter(filt) fmt.Printf("sent filter %x\n", filt.MsgFilterLoad().Filter) + // clear the channel - for len(s.fPositives) != 0 { - fpAccumulator += <-s.fPositives + finClear: + for { + select { + case x := <-s.fPositives: + fpAccumulator += x + + default: + break finClear + } } + fmt.Printf("reset %d false positives\n", fpAccumulator) // reset accumulator fpAccumulator = 0 diff --git a/uspv/txstore.go b/uspv/txstore.go index 3881b485..0cf6b028 100644 --- a/uspv/txstore.go +++ b/uspv/txstore.go @@ -139,6 +139,10 @@ func TxToString(tx *wire.MsgTx) string { for i, in := range tx.TxIn { str += fmt.Sprintf("Input %d: %s\n", i, in.PreviousOutPoint.String()) str += fmt.Sprintf("SigScript for input %d: %x\n", i, in.SignatureScript) + for j, wit := range in.Witness { + str += fmt.Sprintf("witness %d: %x\t", j, wit) + } + str += fmt.Sprintf("\n") } for i, out := range tx.TxOut { if out != nil { @@ -148,12 +152,6 @@ func TxToString(tx *wire.MsgTx) string { str += fmt.Sprintf("output %d nil (WARNING)\n", i) } } - for i, wit := range tx.TxWitness { - if wit != nil { - str += fmt.Sprintf("Witness %d: %x\n", i, wit.ScriptWitness) - } - } - return str }