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
|
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")
|
fmt.Printf("Command not recognized. type help for command list.\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -167,6 +173,26 @@ func Txs(args []string) error {
|
|||||||
return nil
|
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.
|
// Bal prints out your score.
|
||||||
func Bal(args []string) error {
|
func Bal(args []string) error {
|
||||||
if SCon.TS == nil {
|
if SCon.TS == nil {
|
||||||
@ -208,6 +234,9 @@ func Adr(args []string) error {
|
|||||||
|
|
||||||
// Send sends coins.
|
// Send sends coins.
|
||||||
func Send(args []string) error {
|
func Send(args []string) error {
|
||||||
|
if SCon.RBytes == 0 {
|
||||||
|
return fmt.Errorf("Can't send, spv connection broken")
|
||||||
|
}
|
||||||
// get all utxos from the database
|
// get all utxos from the database
|
||||||
allUtxos, err := SCon.TS.GetAllUtxos()
|
allUtxos, err := SCon.TS.GetAllUtxos()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,6 +21,11 @@ const (
|
|||||||
type SPVCon struct {
|
type SPVCon struct {
|
||||||
con net.Conn // the (probably tcp) connection to the node
|
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
|
headerMutex sync.Mutex
|
||||||
headerFile *os.File // file for SPV headers
|
headerFile *os.File // file for SPV headers
|
||||||
|
|
||||||
@ -90,30 +95,34 @@ func (s *SPVCon) RemoveHeaders(r int32) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) error {
|
func (s *SPVCon) IngestMerkleBlock(m *wire.MsgMerkleBlock) {
|
||||||
txids, err := checkMBlock(m) // check self-consistency
|
txids, err := checkMBlock(m) // check self-consistency
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Printf("Merkle block error: %s\n", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
var hah HashAndHeight
|
var hah HashAndHeight
|
||||||
select { // select here so we don't block on an unrequested mblock
|
select { // select here so we don't block on an unrequested mblock
|
||||||
case hah = <-s.mBlockQueue: // pop height off mblock queue
|
case hah = <-s.mBlockQueue: // pop height off mblock queue
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unrequested merkle block")
|
log.Printf("Unrequested merkle block")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// this verifies order, and also that the returned header fits
|
// this verifies order, and also that the returned header fits
|
||||||
// into our SPV header file
|
// into our SPV header file
|
||||||
newMerkBlockSha := m.Header.BlockSha()
|
newMerkBlockSha := m.Header.BlockSha()
|
||||||
if !hah.blockhash.IsEqual(&newMerkBlockSha) {
|
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 {
|
for _, txid := range txids {
|
||||||
err := s.TS.AddTxid(txid, hah.height)
|
err := s.TS.AddTxid(txid, hah.height)
|
||||||
if err != nil {
|
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
|
// 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.
|
// the txs yet but if there are problems with the txs we should backtrack.
|
||||||
err = s.TS.SetDBSyncHeight(hah.height)
|
err = s.TS.SetDBSyncHeight(hah.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Printf("Merkle block error: %s\n", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if hah.final {
|
if hah.final {
|
||||||
// don't set waitstate; instead, ask for headers again!
|
// 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.
|
// that way you are pretty sure you're synced up.
|
||||||
err = s.AskForHeaders()
|
err = s.AskForHeaders()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Printf("Merkle block error: %s\n", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IngestHeaders takes in a bunch of headers and appends them to the
|
// 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 {
|
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) {
|
if left != nil && right != nil && left.IsEqual(right) {
|
||||||
fmt.Printf("DUP HASH CRASH")
|
fmt.Printf("DUP HASH CRASH")
|
||||||
return nil
|
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 {
|
if left == nil {
|
||||||
fmt.Printf("L CRASH")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// if right is nil, has left with itself
|
// if right is nil, hash left with itself
|
||||||
if right == nil {
|
if right == nil {
|
||||||
right = left
|
right = left
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate the left and right nodes
|
// Concatenate the left and right nodes
|
||||||
var sha [wire.HashSize * 2]byte
|
var sha [64]byte
|
||||||
copy(sha[:wire.HashSize], left[:])
|
copy(sha[:32], left[:])
|
||||||
copy(sha[wire.HashSize:], right[:])
|
copy(sha[32:], right[:])
|
||||||
|
|
||||||
newSha := wire.DoubleSha256SH(sha[:])
|
newSha := wire.DoubleSha256SH(sha[:])
|
||||||
return &newSha
|
return &newSha
|
||||||
|
@ -31,11 +31,7 @@ func (s *SPVCon) incomingMessageHandler() {
|
|||||||
case *wire.MsgPong:
|
case *wire.MsgPong:
|
||||||
log.Printf("Got a pong response. OK.\n")
|
log.Printf("Got a pong response. OK.\n")
|
||||||
case *wire.MsgMerkleBlock:
|
case *wire.MsgMerkleBlock:
|
||||||
err = s.IngestMerkleBlock(m)
|
s.IngestMerkleBlock(m)
|
||||||
if err != nil {
|
|
||||||
log.Printf("Merkle block error: %s\n", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case *wire.MsgHeaders: // concurrent because we keep asking for blocks
|
case *wire.MsgHeaders: // concurrent because we keep asking for blocks
|
||||||
go s.HeaderHandler(m)
|
go s.HeaderHandler(m)
|
||||||
case *wire.MsgTx: // not concurrent! txs must be in order
|
case *wire.MsgTx: // not concurrent! txs must be in order
|
||||||
@ -52,6 +48,8 @@ func (s *SPVCon) incomingMessageHandler() {
|
|||||||
}
|
}
|
||||||
case *wire.MsgGetData:
|
case *wire.MsgGetData:
|
||||||
s.GetDataHandler(m)
|
s.GetDataHandler(m)
|
||||||
|
case *wire.MsgBlock:
|
||||||
|
s.IngestBlock(m)
|
||||||
default:
|
default:
|
||||||
log.Printf("Got unknown message type %s\n", m.Command())
|
log.Printf("Got unknown message type %s\n", m.Command())
|
||||||
}
|
}
|
||||||
@ -116,12 +114,16 @@ func (s *SPVCon) HeaderHandler(m *wire.MsgHeaders) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// no moar, done w/ headers, get merkleblocks
|
// no moar, done w/ headers, get merkleblocks
|
||||||
|
if s.HardMode { // in hard mode ask for regular blocks.
|
||||||
|
|
||||||
|
} else {
|
||||||
err = s.AskForMerkBlocks()
|
err = s.AskForMerkBlocks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("AskForMerkBlocks error: %s", err.Error())
|
log.Printf("AskForMerkBlocks error: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TxHandler takes in transaction messages that come in from either a request
|
// TxHandler takes in transaction messages that come in from either a request
|
||||||
// after an inv message or after a merkle block message.
|
// after an inv message or after a merkle block message.
|
||||||
@ -133,6 +135,25 @@ func (s *SPVCon) TxHandler(m *wire.MsgTx) {
|
|||||||
log.Printf("Tx %s unknown, will not ingest\n")
|
log.Printf("Tx %s unknown, will not ingest\n")
|
||||||
return
|
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)
|
hits, err := s.TS.Ingest(m, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Incoming Tx error: %s\n", err.Error())
|
log.Printf("Incoming Tx error: %s\n", err.Error())
|
||||||
@ -176,10 +197,13 @@ func (s *SPVCon) InvHandler(m *wire.MsgInv) {
|
|||||||
for i, thing := range m.InvList {
|
for i, thing := range m.InvList {
|
||||||
log.Printf("\t%d)%s : %s",
|
log.Printf("\t%d)%s : %s",
|
||||||
i, thing.Type.String(), thing.Hash.String())
|
i, thing.Type.String(), thing.Hash.String())
|
||||||
if thing.Type == wire.InvTypeTx { // new tx, OK it at 0 and request
|
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.TS.AddTxid(&thing.Hash, 0) // unconfirmed
|
||||||
s.AskForTx(thing.Hash)
|
s.AskForTx(thing.Hash)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if thing.Type == wire.InvTypeBlock { // new block what to do?
|
if thing.Type == wire.InvTypeBlock { // new block what to do?
|
||||||
select {
|
select {
|
||||||
case <-s.inWaitState:
|
case <-s.inWaitState:
|
||||||
|
@ -103,7 +103,7 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) {
|
|||||||
// GetDoubleSpends takes a transaction and compares it with
|
// GetDoubleSpends takes a transaction and compares it with
|
||||||
// all transactions in the db. It returns a slice of all txids in the db
|
// all transactions in the db. It returns a slice of all txids in the db
|
||||||
// which are double spent by the received tx.
|
// which are double spent by the received tx.
|
||||||
func GetDoubleSpends(
|
func CheckDoubleSpends(
|
||||||
argTx *wire.MsgTx, txs []*wire.MsgTx) ([]*wire.ShaHash, error) {
|
argTx *wire.MsgTx, txs []*wire.MsgTx) ([]*wire.ShaHash, error) {
|
||||||
|
|
||||||
var dubs []*wire.ShaHash // slice of all double-spent txs
|
var dubs []*wire.ShaHash // slice of all double-spent txs
|
||||||
|
Loading…
Reference in New Issue
Block a user