mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-20 02:27:21 +01:00
add hard mode test methods
really simple to check block merkle root next can modify "ingestMerkleBlock" to ingest either type of block.
This commit is contained in:
parent
63b5926e01
commit
8518be13f7
31
shell.go
31
shell.go
@ -151,7 +151,13 @@ func Shellparse(cmdslice []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if cmd == "blk" {
|
||||
err = Blk(args)
|
||||
if err != nil {
|
||||
fmt.Printf("blk error: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("Command not recognized. type help for command list.\n")
|
||||
return nil
|
||||
}
|
||||
@ -167,6 +173,26 @@ func Txs(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Blk(args []string) error {
|
||||
if SCon.RBytes == 0 {
|
||||
return fmt.Errorf("Can't check block, spv connection broken")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("must specify height")
|
||||
}
|
||||
height, err := strconv.ParseInt(args[0], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// request most recent block just to test
|
||||
err = SCon.AskForOneBlock(int32(height))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bal prints out your score.
|
||||
func Bal(args []string) error {
|
||||
if SCon.TS == nil {
|
||||
@ -208,6 +234,9 @@ func Adr(args []string) error {
|
||||
|
||||
// Send sends coins.
|
||||
func Send(args []string) error {
|
||||
if SCon.RBytes == 0 {
|
||||
return fmt.Errorf("Can't send, spv connection broken")
|
||||
}
|
||||
// get all utxos from the database
|
||||
allUtxos, err := SCon.TS.GetAllUtxos()
|
||||
if err != nil {
|
||||
|
@ -21,6 +21,11 @@ const (
|
||||
type SPVCon struct {
|
||||
con net.Conn // the (probably tcp) connection to the node
|
||||
|
||||
// Enhanced SPV modes for users who have outgrown easy mode SPV
|
||||
// but have not yet graduated to full nodes.
|
||||
HardMode bool // hard mode doesn't use filters.
|
||||
Ironman bool // ironman only gets blocks, never requests txs.
|
||||
|
||||
headerMutex sync.Mutex
|
||||
headerFile *os.File // file for SPV headers
|
||||
|
||||
@ -90,30 +95,34 @@ func (s *SPVCon) RemoveHeaders(r int32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) error {
|
||||
func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) {
|
||||
txids, err := checkMBlock(m) // check self-consistency
|
||||
if err != nil {
|
||||
return err
|
||||
log.Printf("Merkle block error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
var hah HashAndHeight
|
||||
select { // select here so we don't block on an unrequested mblock
|
||||
case hah = <-s.mBlockQueue: // pop height off mblock queue
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("Unrequested merkle block")
|
||||
log.Printf("Unrequested merkle block")
|
||||
return
|
||||
}
|
||||
|
||||
// this verifies order, and also that the returned header fits
|
||||
// into our SPV header file
|
||||
newMerkBlockSha := m.Header.BlockSha()
|
||||
if !hah.blockhash.IsEqual(&newMerkBlockSha) {
|
||||
return fmt.Errorf("merkle block out of order error")
|
||||
log.Printf("merkle block out of order error")
|
||||
return
|
||||
}
|
||||
|
||||
for _, txid := range txids {
|
||||
err := s.TS.AddTxid(txid, hah.height)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Txid store error: %s\n", err.Error())
|
||||
log.Printf("Txid store error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
// write to db that we've sync'd to the height indicated in the
|
||||
@ -121,7 +130,8 @@ func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) error {
|
||||
// the txs yet but if there are problems with the txs we should backtrack.
|
||||
err = s.TS.SetDBSyncHeight(hah.height)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Printf("Merkle block error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
if hah.final {
|
||||
// don't set waitstate; instead, ask for headers again!
|
||||
@ -130,11 +140,11 @@ func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) error {
|
||||
// that way you are pretty sure you're synced up.
|
||||
err = s.AskForHeaders()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Printf("Merkle block error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// IngestHeaders takes in a bunch of headers and appends them to the
|
||||
|
84
uspv/hardmode.go
Normal file
84
uspv/hardmode.go
Normal file
@ -0,0 +1,84 @@
|
||||
package uspv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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 {
|
||||
fmt.Printf("BlockRootOK for block %s\n", blk.BlockSha().String())
|
||||
|
||||
var shas []*wire.ShaHash
|
||||
for _, tx := range blk.Transactions { // make slice of txids
|
||||
nSha := tx.TxSha()
|
||||
shas = append(shas, &nSha)
|
||||
}
|
||||
|
||||
// pad out tx slice to get the full tree base
|
||||
neededLen := int(nextPowerOfTwo(uint32(len(shas)))) // kindof ugly
|
||||
for len(shas) < neededLen {
|
||||
shas = append(shas, nil)
|
||||
}
|
||||
fmt.Printf("Padded %d txs in block to %d\n",
|
||||
len(blk.Transactions), len(shas))
|
||||
|
||||
// calculate merkle root. Terse, eh?
|
||||
for len(shas) > 1 {
|
||||
shas = append(shas[2:], MakeMerkleParent(shas[0], shas[1]))
|
||||
}
|
||||
|
||||
fmt.Printf("calc'd mroot %s, %s in header\n",
|
||||
shas[0].String(), blk.Header.MerkleRoot.String())
|
||||
|
||||
if blk.Header.MerkleRoot.IsEqual(shas[0]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SPVCon) IngestBlock(m *wire.MsgBlock) {
|
||||
ok := BlockRootOK(*m) // check self-consistency
|
||||
if !ok {
|
||||
fmt.Printf("block %s not OK!!11\n", m.BlockSha().String())
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SPVCon) AskForOneBlock(h int32) error {
|
||||
var hdr wire.BlockHeader
|
||||
var err error
|
||||
|
||||
dbTip := int32(h)
|
||||
s.headerMutex.Lock() // seek to header we need
|
||||
_, err = s.headerFile.Seek(int64((dbTip)*80), os.SEEK_SET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = hdr.Deserialize(s.headerFile) // read header, done w/ file for now
|
||||
s.headerMutex.Unlock() // unlock after reading 1 header
|
||||
if err != nil {
|
||||
log.Printf("header deserialize error!\n")
|
||||
return err
|
||||
}
|
||||
|
||||
bHash := hdr.BlockSha()
|
||||
// create inventory we're asking for
|
||||
iv1 := wire.NewInvVect(wire.InvTypeBlock, &bHash)
|
||||
gdataMsg := wire.NewMsgGetData()
|
||||
// add inventory
|
||||
err = gdataMsg.AddInvVect(iv1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.outMsgQueue <- gdataMsg
|
||||
|
||||
return nil
|
||||
}
|
@ -7,25 +7,24 @@ import (
|
||||
)
|
||||
|
||||
func MakeMerkleParent(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash {
|
||||
// this can screw things up; CVE-2012-2459
|
||||
// dupes can screw things up; CVE-2012-2459. check for them
|
||||
if left != nil && right != nil && left.IsEqual(right) {
|
||||
fmt.Printf("DUP HASH CRASH")
|
||||
return nil
|
||||
}
|
||||
// if left chils is nil, output nil. Shouldn't need this?
|
||||
// if left child is nil, output nil. Need this for hard mode.
|
||||
if left == nil {
|
||||
fmt.Printf("L CRASH")
|
||||
return nil
|
||||
}
|
||||
// if right is nil, has left with itself
|
||||
// if right is nil, hash left with itself
|
||||
if right == nil {
|
||||
right = left
|
||||
}
|
||||
|
||||
// Concatenate the left and right nodes
|
||||
var sha [wire.HashSize * 2]byte
|
||||
copy(sha[:wire.HashSize], left[:])
|
||||
copy(sha[wire.HashSize:], right[:])
|
||||
var sha [64]byte
|
||||
copy(sha[:32], left[:])
|
||||
copy(sha[32:], right[:])
|
||||
|
||||
newSha := wire.DoubleSha256SH(sha[:])
|
||||
return &newSha
|
||||
|
@ -31,11 +31,7 @@ func (s *SPVCon) incomingMessageHandler() {
|
||||
case *wire.MsgPong:
|
||||
log.Printf("Got a pong response. OK.\n")
|
||||
case *wire.MsgMerkleBlock:
|
||||
err = s.IngestMerkleBlock(m)
|
||||
if err != nil {
|
||||
log.Printf("Merkle block error: %s\n", err.Error())
|
||||
continue
|
||||
}
|
||||
s.IngestMerkleBlock(m)
|
||||
case *wire.MsgHeaders: // concurrent because we keep asking for blocks
|
||||
go s.HeaderHandler(m)
|
||||
case *wire.MsgTx: // not concurrent! txs must be in order
|
||||
@ -52,6 +48,8 @@ func (s *SPVCon) incomingMessageHandler() {
|
||||
}
|
||||
case *wire.MsgGetData:
|
||||
s.GetDataHandler(m)
|
||||
case *wire.MsgBlock:
|
||||
s.IngestBlock(m)
|
||||
default:
|
||||
log.Printf("Got unknown message type %s\n", m.Command())
|
||||
}
|
||||
@ -116,10 +114,14 @@ func (s *SPVCon) HeaderHandler(m *wire.MsgHeaders) {
|
||||
return
|
||||
}
|
||||
// no moar, done w/ headers, get merkleblocks
|
||||
err = s.AskForMerkBlocks()
|
||||
if err != nil {
|
||||
log.Printf("AskForMerkBlocks error: %s", err.Error())
|
||||
return
|
||||
if s.HardMode { // in hard mode ask for regular blocks.
|
||||
|
||||
} else {
|
||||
err = s.AskForMerkBlocks()
|
||||
if err != nil {
|
||||
log.Printf("AskForMerkBlocks error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +135,25 @@ func (s *SPVCon) TxHandler(m *wire.MsgTx) {
|
||||
log.Printf("Tx %s unknown, will not ingest\n")
|
||||
return
|
||||
}
|
||||
|
||||
// check for double spends
|
||||
allTxs, err := s.TS.GetAllTxs()
|
||||
if err != nil {
|
||||
log.Printf("Can't get txs from db: %s", err.Error())
|
||||
return
|
||||
}
|
||||
dubs, err := CheckDoubleSpends(m, allTxs)
|
||||
if err != nil {
|
||||
log.Printf("CheckDoubleSpends error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if len(dubs) > 0 {
|
||||
for i, dub := range dubs {
|
||||
fmt.Printf("dub %d known tx %s and new tx %s are exclusive!!!\n",
|
||||
i, dub.String(), m.TxSha().String())
|
||||
}
|
||||
}
|
||||
|
||||
hits, err := s.TS.Ingest(m, height)
|
||||
if err != nil {
|
||||
log.Printf("Incoming Tx error: %s\n", err.Error())
|
||||
@ -176,9 +197,12 @@ func (s *SPVCon) InvHandler(m *wire.MsgInv) {
|
||||
for i, thing := range m.InvList {
|
||||
log.Printf("\t%d)%s : %s",
|
||||
i, thing.Type.String(), thing.Hash.String())
|
||||
if thing.Type == wire.InvTypeTx { // new tx, OK it at 0 and request
|
||||
s.TS.AddTxid(&thing.Hash, 0) // unconfirmed
|
||||
s.AskForTx(thing.Hash)
|
||||
if thing.Type == wire.InvTypeTx {
|
||||
if !s.Ironman { // ignore tx invs in ironman mode
|
||||
// new tx, OK it at 0 and request
|
||||
s.TS.AddTxid(&thing.Hash, 0) // unconfirmed
|
||||
s.AskForTx(thing.Hash)
|
||||
}
|
||||
}
|
||||
if thing.Type == wire.InvTypeBlock { // new block what to do?
|
||||
select {
|
||||
|
@ -103,7 +103,7 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) {
|
||||
// GetDoubleSpends takes a transaction and compares it with
|
||||
// all transactions in the db. It returns a slice of all txids in the db
|
||||
// which are double spent by the received tx.
|
||||
func GetDoubleSpends(
|
||||
func CheckDoubleSpends(
|
||||
argTx *wire.MsgTx, txs []*wire.MsgTx) ([]*wire.ShaHash, error) {
|
||||
|
||||
var dubs []*wire.ShaHash // slice of all double-spent txs
|
||||
|
Loading…
Reference in New Issue
Block a user