示例#1
0
文件: hdrs.go 项目: liudch/gocoin
func chkblock(bl *btc.Block) (er error) {
	// Check timestamp (must not be higher than now +2 hours)
	if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 {
		er = errors.New("CheckBlock() : block timestamp too far in the future")
		return
	}

	MemBlockChainMutex.Lock()
	if prv, pres := MemBlockChain.BlockIndex[bl.Hash.BIdx()]; pres {
		MemBlockChainMutex.Unlock()
		if prv.Parent == nil {
			// This is genesis block
			er = errors.New("Genesis")
			return
		} else {
			return
		}
	}

	prevblk, ok := MemBlockChain.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()]
	if !ok {
		er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found")
		return
	}

	// Check proof of work
	gnwr := MemBlockChain.GetNextWorkRequired(prevblk, bl.BlockTime())
	if bl.Bits() != gnwr {
		if !Testnet || ((prevblk.Height+1)%2016) != 0 {
			MemBlockChainMutex.Unlock()
			er = errors.New(fmt.Sprint("CheckBlock: Incorrect proof of work at block", prevblk.Height+1))
			return
		}
	}

	cur := new(chain.BlockTreeNode)
	cur.BlockHash = bl.Hash
	cur.Parent = prevblk
	cur.Height = prevblk.Height + 1
	cur.TxCount = uint32(bl.TxCount)
	copy(cur.BlockHeader[:], bl.Raw[:80])
	prevblk.Childs = append(prevblk.Childs, cur)
	MemBlockChain.BlockIndex[cur.BlockHash.BIdx()] = cur
	MemBlockChainMutex.Unlock()

	LastBlock.Mutex.Lock()
	if cur.Height > LastBlock.node.Height {
		LastBlock.node = cur
	}
	LastBlock.Mutex.Unlock()

	return
}
示例#2
0
// This isusually the most time consuming process when applying a new block
func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) {
	sumblockin := btc.GetBlockReward(changes.Height)
	var txoutsum, txinsum, sumblockout uint64

	// Add each tx outs from the current block to the temporary pool
	blUnsp := make(map[[32]byte][]*btc.TxOut, len(bl.Txs))
	for i := range bl.Txs {
		outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut))
		for j := range bl.Txs[i].TxOut {
			bl.Txs[i].TxOut[j].BlockHeight = changes.Height
			outs[j] = bl.Txs[i].TxOut[j]
		}
		blUnsp[bl.Txs[i].Hash.Hash] = outs
	}

	// create a channnel to receive results from VerifyScript threads:
	done := make(chan bool, sys.UseThreads)

	for i := range bl.Txs {
		txoutsum, txinsum = 0, 0

		// Check each tx for a valid input, except from the first one
		if i > 0 {
			tx_trusted := bl.Trusted
			if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) {
				tx_trusted = true
			}

			scripts_ok := true

			for j := 0; j < sys.UseThreads; j++ {
				done <- true
			}

			for j := 0; j < len(bl.Txs[i].TxIn); /*&& e==nil*/ j++ {
				inp := &bl.Txs[i].TxIn[j].Input
				if _, ok := changes.DeledTxs[*inp]; ok {
					println("txin", inp.String(), "already spent in this block")
					e = errors.New("Input spent more then once in same block")
					break
				}
				tout := ch.PickUnspent(inp)
				if tout == nil {
					t, ok := blUnsp[inp.Hash]
					if !ok {
						e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String())
						break
					}

					if inp.Vout >= uint32(len(t)) {
						println("Vout too big", len(t), inp.String())
						e = errors.New("Vout too big")
						break
					}

					if t[inp.Vout] == nil {
						println("Vout already spent", inp.String())
						e = errors.New("Vout already spent")
						break
					}

					tout = t[inp.Vout]
					t[inp.Vout] = nil // and now mark it as spent:
				}

				if !(<-done) {
					println("VerifyScript error 1")
					scripts_ok = false
					break
				}

				if tx_trusted {
					done <- true
				} else {
					go func(sig []byte, prv []byte, i int, tx *btc.Tx) {
						done <- script.VerifyTxScript(sig, prv, i, tx, bl.BlockTime() >= BIP16SwitchTime)
					}(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i])
				}

				// Verify Transaction script:
				txinsum += tout.Value
				changes.DeledTxs[*inp] = tout
			}

			if scripts_ok {
				scripts_ok = <-done
			}
			for j := 1; j < sys.UseThreads; j++ {
				if !(<-done) {
					println("VerifyScript error 2")
					scripts_ok = false
				}
			}
			if len(done) != 0 {
				panic("ASSERT: The channel should be empty gere")
			}

			if !scripts_ok {
				return errors.New("VerifyScripts failed")
			}
		} else {
			// For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes
			// (Previously we made sure in CheckBlock() that this was a coinbase type tx)
			if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 {
				return errors.New(fmt.Sprint("Coinbase script has a wrong length", len(bl.Txs[0].TxIn[0].ScriptSig)))
			}
		}
		sumblockin += txinsum

		for j := range bl.Txs[i].TxOut {
			txoutsum += bl.Txs[i].TxOut[j].Value
			txa := new(btc.TxPrevOut)
			copy(txa.Hash[:], bl.Txs[i].Hash.Hash[:])
			txa.Vout = uint32(j)
			_, spent := changes.DeledTxs[*txa]
			if spent {
				delete(changes.DeledTxs, *txa)
			} else {
				changes.AddedTxs[*txa] = bl.Txs[i].TxOut[j]
			}
		}
		sumblockout += txoutsum

		if e != nil {
			return // If any input fails, do not continue
		}
		if i > 0 && txoutsum > txinsum {
			return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s",
				float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String()))
		}
	}

	if sumblockin < sumblockout {
		return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin))
	}

	return nil
}
示例#3
0
// This isusually the most time consuming process when applying a new block
func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) {
	sumblockin := btc.GetBlockReward(changes.Height)
	var txoutsum, txinsum, sumblockout uint64

	if int(changes.Height)+UnwindBufferMaxHistory >= int(changes.LastKnownHeight) {
		changes.UndoData = make(map[[32]byte]*QdbRec)
	}

	// Add each tx outs from the current block to the temporary pool
	blUnsp := make(map[[32]byte][]*btc.TxOut, 4*len(bl.Txs))
	for i := range bl.Txs {
		outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut))
		copy(outs, bl.Txs[i].TxOut)
		blUnsp[bl.Txs[i].Hash.Hash] = outs
	}

	// create a channnel to receive results from VerifyScript threads:
	done := make(chan bool, sys.UseThreads)

	now := changes.Height == 381 && false
	//println("pr", changes.Height)

	for i := range bl.Txs {
		txoutsum, txinsum = 0, 0

		// Check each tx for a valid input, except from the first one
		if i > 0 {
			tx_trusted := bl.Trusted
			if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) {
				tx_trusted = true
			}

			scripts_ok := true

			for j := 0; j < sys.UseThreads; j++ {
				done <- true
			}

			for j := 0; j < len(bl.Txs[i].TxIn); /*&& e==nil*/ j++ {
				inp := &bl.Txs[i].TxIn[j].Input
				spendrec, waspent := changes.DeledTxs[inp.Hash]
				if waspent && spendrec[inp.Vout] {
					println("txin", inp.String(), "already spent in this block")
					e = errors.New("Input spent more then once in same block")
					break
				}
				tout := ch.PickUnspent(inp)
				if tout == nil {
					t, ok := blUnsp[inp.Hash]
					if !ok {
						e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String())
						break
					}

					if inp.Vout >= uint32(len(t)) {
						println("Vout too big", len(t), inp.String())
						e = errors.New("Vout too big")
						break
					}

					if t[inp.Vout] == nil {
						println("Vout already spent", inp.String())
						e = errors.New("Vout already spent")
						break
					}

					if t[inp.Vout].WasCoinbase {
						e = errors.New("Cannot spend block's own coinbase in TxID: " + btc.NewUint256(inp.Hash[:]).String())
						break
					}

					tout = t[inp.Vout]
					t[inp.Vout] = nil // and now mark it as spent:
				} else {
					if tout.WasCoinbase && changes.Height-tout.BlockHeight < COINBASE_MATURITY {
						e = errors.New("Trying to spend prematured coinbase: " + btc.NewUint256(inp.Hash[:]).String())
						break
					}
					// it is confirmed already so delete it later
					if !waspent {
						spendrec = make([]bool, tout.VoutCount)
						changes.DeledTxs[inp.Hash] = spendrec
					}
					spendrec[inp.Vout] = true

					if changes.UndoData != nil {
						var urec *QdbRec
						urec = changes.UndoData[inp.Hash]
						if urec == nil {
							urec = new(QdbRec)
							urec.TxID = inp.Hash
							urec.Coinbase = tout.WasCoinbase
							urec.InBlock = tout.BlockHeight
							urec.Outs = make([]*QdbTxOut, tout.VoutCount)
							changes.UndoData[inp.Hash] = urec
						}
						tmp := new(QdbTxOut)
						tmp.Value = tout.Value
						tmp.PKScr = make([]byte, len(tout.Pk_script))
						copy(tmp.PKScr, tout.Pk_script)
						urec.Outs[inp.Vout] = tmp
					}
				}

				if !(<-done) {
					println("VerifyScript error 1")
					scripts_ok = false
					break
				}

				if tx_trusted {
					done <- true
				} else {
					go func(sig []byte, prv []byte, i int, tx *btc.Tx) {
						done <- script.VerifyTxScript(sig, prv, i, tx, bl.BlockTime() >= BIP16SwitchTime)
					}(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i])
				}

				// Verify Transaction script:
				if now {
					println("+", inp.String(), tout.Value)
				}
				txinsum += tout.Value
			}

			if scripts_ok {
				scripts_ok = <-done
			}
			for j := 1; j < sys.UseThreads; j++ {
				if !(<-done) {
					println("VerifyScript error 2")
					scripts_ok = false
				}
			}
			if len(done) != 0 {
				panic("ASSERT: The channel should be empty gere")
			}

			if !scripts_ok {
				return errors.New("VerifyScripts failed")
			}
		} else {
			// For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes
			// (Previously we made sure in CheckBlock() that this was a coinbase type tx)
			if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 {
				return errors.New(fmt.Sprint("Coinbase script has a wrong length", len(bl.Txs[0].TxIn[0].ScriptSig)))
			}
		}
		sumblockin += txinsum

		for j := range bl.Txs[i].TxOut {
			txoutsum += bl.Txs[i].TxOut[j].Value
		}
		sumblockout += txoutsum

		if e != nil {
			return // If any input fails, do not continue
		}
		if i > 0 && txoutsum > txinsum {
			return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s",
				float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String()))
		}
	}

	if sumblockin < sumblockout {
		return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin))
	}

	var rec *QdbRec
	for k, v := range blUnsp {
		for i := range v {
			if v[i] != nil {
				if rec == nil {
					rec = new(QdbRec)
					rec.TxID = k
					rec.Coinbase = v[i].WasCoinbase
					rec.InBlock = changes.Height
					rec.Outs = make([]*QdbTxOut, len(v))
				}
				rec.Outs[i] = &QdbTxOut{Value: v[i].Value, PKScr: v[i].Pk_script}
			}
		}
		if rec != nil {
			changes.AddList = append(changes.AddList, rec)
			rec = nil
		}
	}

	return nil
}
示例#4
0
文件: main.go 项目: rollyleal/gocoin
func LocalAcceptBlock(bl *btc.Block, from *network.OneConnection) (e error) {
	sta := time.Now()
	e = common.BlockChain.AcceptBlock(bl)
	if e == nil {
		network.MutexRcv.Lock()
		network.ReceivedBlocks[bl.Hash.BIdx()].TmAccept = time.Now().Sub(sta)
		network.MutexRcv.Unlock()

		for i := 1; i < len(bl.Txs); i++ {
			network.TxMined(bl.Txs[i])
			/*
				if msg:=contains_message(bl.Txs[i]); msg!=nil {
					for xx:=range msg {
						if msg[xx]<' ' || msg[xx]>127 {
							msg[xx] = '.'
						}
					}
					fmt.Println("TX", bl.Txs[i].Hash.String(), "says:", "'" + string(msg) + "'")
					textui.ShowPrompt()
				}
			*/
		}

		if int64(bl.BlockTime()) > time.Now().Add(-10*time.Minute).Unix() {
			// Freshly mined block - do the inv and beeps...
			common.Busy("NetRouteInv")
			network.NetRouteInv(2, bl.Hash, from)

			if common.CFG.Beeps.NewBlock {
				fmt.Println("\007Received block", common.BlockChain.BlockTreeEnd.Height)
				textui.ShowPrompt()
			}

			if common.MinedByUs(bl.Raw) {
				fmt.Println("\007Mined by '"+common.CFG.Beeps.MinerID+"':", bl.Hash)
				textui.ShowPrompt()
			}

			if common.CFG.Beeps.ActiveFork && common.Last.Block == common.BlockChain.BlockTreeEnd {
				// Last block has not changed, so it must have been an orphaned block
				bln := common.BlockChain.BlockIndex[bl.Hash.BIdx()]
				commonNode := common.Last.Block.FirstCommonParent(bln)
				forkDepth := bln.Height - commonNode.Height
				fmt.Println("Orphaned block:", bln.Height, bl.Hash.String(), bln.BlockSize>>10, "KB")
				if forkDepth > 1 {
					fmt.Println("\007\007\007WARNING: the fork is", forkDepth, "blocks deep")
				}
				textui.ShowPrompt()
			}

			if wallet.BalanceChanged && common.CFG.Beeps.NewBalance {
				fmt.Print("\007")
			}
		}

		common.Last.Mutex.Lock()
		common.Last.Time = time.Now()
		common.Last.Block = common.BlockChain.BlockTreeEnd
		common.Last.Mutex.Unlock()

		if wallet.BalanceChanged {
			wallet.BalanceChanged = false
			fmt.Println("Your balance has just changed")
			fmt.Print(wallet.DumpBalance(wallet.MyBalance, nil, false, true))
			textui.ShowPrompt()
		}
	} else {
		fmt.Println("Warning: AcceptBlock failed. If the block was valid, you may need to rebuild the unspent DB (-r)")
	}
	return
}
示例#5
0
func (ch *Chain) CheckBlock(bl *btc.Block) (er error, dos bool, maybelater bool) {
	// Size limits
	if len(bl.Raw) < 81 || len(bl.Raw) > btc.MAX_BLOCK_SIZE {
		er = errors.New("CheckBlock() : size limits failed")
		dos = true
		return
	}

	// Check timestamp (must not be higher than now +2 hours)
	if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 {
		er = errors.New("CheckBlock() : block timestamp too far in the future")
		dos = true
		return
	}

	if prv, pres := ch.BlockIndex[bl.Hash.BIdx()]; pres {
		if prv.Parent == nil {
			// This is genesis block
			er = errors.New("Genesis")
			return
		} else {
			er = errors.New("CheckBlock: " + bl.Hash.String() + " already in")
			return
		}
	}

	prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()]
	if !ok {
		er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found")
		maybelater = true
		return
	}

	// Reject the block if it reaches into the chain deeper than our unwind buffer
	if prevblk != ch.BlockTreeEnd && int(ch.BlockTreeEnd.Height)-int(prevblk.Height+1) >= MovingCheckopintDepth {
		er = errors.New(fmt.Sprint("CheckBlock: btc.Block ", bl.Hash.String(),
			" hooks too deep into the chain: ", prevblk.Height+1, "/", ch.BlockTreeEnd.Height, " ",
			btc.NewUint256(bl.ParentHash()).String()))
		return
	}

	// Check proof of work
	gnwr := ch.GetNextWorkRequired(prevblk, bl.BlockTime())
	if bl.Bits() != gnwr {
		println("AcceptBlock() : incorrect proof of work ", bl.Bits, " at block", prevblk.Height+1,
			" exp:", gnwr)

		// Here is a "solution" for whatever shit there is in testnet3, that nobody can explain me:
		if !ch.testnet() || ((prevblk.Height+1)%2016) != 0 {
			er = errors.New("CheckBlock: incorrect proof of work")
			dos = true
			return
		}
	}

	if bl.Txs == nil {
		er = bl.BuildTxList()
		if er != nil {
			dos = true
			return
		}
	}

	if !bl.Trusted {
		// This is a stupid check, but well, we need to be satoshi compatible
		if len(bl.Txs) == 0 || !bl.Txs[0].IsCoinBase() {
			er = errors.New("CheckBlock() : first tx is not coinbase: " + bl.Hash.String())
			dos = true
			return
		}

		// Check Merkle Root - that's importnant
		if !bytes.Equal(btc.GetMerkel(bl.Txs), bl.MerkleRoot()) {
			er = errors.New("CheckBlock() : Merkle Root mismatch")
			dos = true
			return
		}

		// Check transactions - this is the most time consuming task
		for i := 0; i < len(bl.Txs); i++ {
			er = bl.Txs[i].CheckTransaction()
			if er != nil {
				er = errors.New("CheckBlock() : CheckTransaction failed: " + er.Error())
				dos = true
				return
			}
			if !bl.Txs[i].IsFinal(prevblk.Height+1, bl.BlockTime()) {
				er = errors.New("CheckBlock() : Contains transaction that is not final")
				return
			}
		}
	}

	return
}
示例#6
0
func (ch *Chain) CheckBlock(bl *btc.Block) (er error, dos bool, maybelater bool) {
	// Size limits
	if len(bl.Raw) < 81 || len(bl.Raw) > btc.MAX_BLOCK_SIZE {
		er = errors.New("CheckBlock() : size limits failed")
		dos = true
		return
	}

	if bl.Version() == 0 {
		er = errors.New("CheckBlock() : Block version 0 not allowed")
		dos = true
		return
	}

	// Check timestamp (must not be higher than now +2 hours)
	if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 {
		er = errors.New("CheckBlock() : block timestamp too far in the future")
		dos = true
		return
	}

	if prv, pres := ch.BlockIndex[bl.Hash.BIdx()]; pres {
		if prv.Parent == nil {
			// This is genesis block
			er = errors.New("Genesis")
			return
		} else {
			er = errors.New("CheckBlock: " + bl.Hash.String() + " already in")
			return
		}
	}

	prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()]
	if !ok {
		er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found")
		maybelater = true
		return
	}

	height := prevblk.Height + 1

	// Reject the block if it reaches into the chain deeper than our unwind buffer
	if prevblk != ch.BlockTreeEnd && int(ch.BlockTreeEnd.Height)-int(height) >= MovingCheckopintDepth {
		er = errors.New(fmt.Sprint("CheckBlock: btc.Block ", bl.Hash.String(),
			" hooks too deep into the chain: ", height, "/", ch.BlockTreeEnd.Height, " ",
			btc.NewUint256(bl.ParentHash()).String()))
		return
	}

	// Check proof of work
	gnwr := ch.GetNextWorkRequired(prevblk, bl.BlockTime())
	if bl.Bits() != gnwr {
		println("AcceptBlock() : incorrect proof of work ", bl.Bits, " at block", height, " exp:", gnwr)

		// Here is a "solution" for whatever shit there is in testnet3, that nobody can explain me:
		if !ch.testnet() || (height%2016) != 0 {
			er = errors.New("CheckBlock: incorrect proof of work")
			dos = true
			return
		}
	}

	// Count block versions within the Majority Window
	var majority_v2, majority_v3 uint
	n := prevblk
	for cnt := uint(0); cnt < ch.Consensus.Window && n != nil; cnt++ {
		ver := binary.LittleEndian.Uint32(n.BlockHeader[0:4])
		if ver >= 2 {
			majority_v2++
			if ver >= 3 {
				majority_v3++
			}
		}
		n = n.Parent
	}

	if bl.Version() < 2 && majority_v2 >= ch.Consensus.RejectBlock {
		er = errors.New("CheckBlock() : Rejected nVersion=1 block")
		dos = true
		return
	}

	if bl.Version() < 3 && majority_v3 >= ch.Consensus.RejectBlock {
		er = errors.New("CheckBlock() : Rejected nVersion=2 block")
		dos = true
		return
	}

	if bl.Txs == nil {
		er = bl.BuildTxList()
		if er != nil {
			dos = true
			return
		}
	}

	if !bl.Trusted {
		if bl.Version() >= 2 && majority_v2 >= ch.Consensus.EnforceUpgrade {
			var exp []byte
			if height >= 0x800000 {
				if height >= 0x80000000 {
					exp = []byte{5, byte(height), byte(height >> 8), byte(height >> 16), byte(height >> 24), 0}
				} else {
					exp = []byte{4, byte(height), byte(height >> 8), byte(height >> 16), byte(height >> 24)}
				}
			} else {
				exp = []byte{3, byte(height), byte(height >> 8), byte(height >> 16)}
			}
			if len(bl.Txs[0].TxIn[0].ScriptSig) < len(exp) || !bytes.Equal(exp, bl.Txs[0].TxIn[0].ScriptSig[:len(exp)]) {
				er = errors.New("CheckBlock() : Unexpected block number in coinbase: " + bl.Hash.String())
				dos = true
				return
			}
		}

		// This is a stupid check, but well, we need to be satoshi compatible
		if len(bl.Txs) == 0 || !bl.Txs[0].IsCoinBase() {
			er = errors.New("CheckBlock() : first tx is not coinbase: " + bl.Hash.String())
			dos = true
			return
		}

		// Check Merkle Root - that's importnant
		if !bytes.Equal(btc.GetMerkel(bl.Txs), bl.MerkleRoot()) {
			er = errors.New("CheckBlock() : Merkle Root mismatch")
			dos = true
			return
		}

		// Check transactions - this is the most time consuming task
		if !CheckTransactions(bl.Txs, height, bl.BlockTime()) {
			er = errors.New("CheckBlock() : CheckTransactions() failed")
			dos = true
			return
		}
	}

	if bl.BlockTime() >= BIP16SwitchTime {
		bl.VerifyFlags = script.VER_P2SH
	} else {
		bl.VerifyFlags = 0
	}

	if majority_v3 >= ch.Consensus.EnforceUpgrade {
		bl.VerifyFlags |= script.VER_DERSIG
	}

	return
}
示例#7
0
func (ch *Chain) PostCheckBlock(bl *btc.Block) (er error) {
	// Size limits
	if len(bl.Raw) < 81 {
		er = errors.New("CheckBlock() : size limits failed low - RPC_Result:bad-blk-length")
		return
	}

	if bl.Txs == nil {
		er = bl.BuildTxList()
		if er != nil {
			return
		}
		if len(bl.OldData) > btc.MAX_BLOCK_SIZE {
			er = errors.New("CheckBlock() : size limits failed high - RPC_Result:bad-blk-length")
			return
		}
	}

	if !bl.Trusted {
		// We need to be satoshi compatible
		if len(bl.Txs) == 0 || !bl.Txs[0].IsCoinBase() {
			er = errors.New("CheckBlock() : first tx is not coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-missing")
			return
		}

		// Enforce rule that the coinbase starts with serialized block height
		if bl.Height >= ch.Consensus.BIP34Height {
			var exp [6]byte
			var exp_len int
			binary.LittleEndian.PutUint32(exp[1:5], bl.Height)
			for exp_len = 5; exp_len > 1; exp_len-- {
				if exp[exp_len] != 0 || exp[exp_len-1] >= 0x80 {
					break
				}
			}
			exp[0] = byte(exp_len)
			exp_len++

			if !bytes.HasPrefix(bl.Txs[0].TxIn[0].ScriptSig, exp[:exp_len]) {
				er = errors.New("CheckBlock() : Unexpected block number in coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-height")
				return
			}
		}

		// And again...
		for i := 1; i < len(bl.Txs); i++ {
			if bl.Txs[i].IsCoinBase() {
				er = errors.New("CheckBlock() : more than one coinbase: " + bl.Hash.String() + " - RPC_Result:bad-cb-multiple")
				return
			}
		}

		// Check Merkle Root - that's importnant
		merkle, mutated := btc.GetMerkle(bl.Txs)
		if mutated {
			er = errors.New("CheckBlock(): duplicate transaction - RPC_Result:bad-txns-duplicate")
			return
		}

		if !bytes.Equal(merkle, bl.MerkleRoot()) {
			er = errors.New("CheckBlock() : Merkle Root mismatch - RPC_Result:bad-txnmrklroot")
			return
		}
	}

	if bl.BlockTime() >= BIP16SwitchTime {
		bl.VerifyFlags = script.VER_P2SH
	} else {
		bl.VerifyFlags = 0
	}

	if bl.Height >= ch.Consensus.BIP66Height {
		bl.VerifyFlags |= script.VER_DERSIG
	}

	if bl.Height >= ch.Consensus.BIP65Height {
		bl.VerifyFlags |= script.VER_CLTV
	}

	if ch.Consensus.Enforce_CSV != 0 && bl.Height >= ch.Consensus.Enforce_CSV {
		bl.VerifyFlags |= script.VER_CSV
	}

	if ch.Consensus.Enforce_SEGWIT != 0 && bl.Height >= ch.Consensus.Enforce_SEGWIT {
		bl.VerifyFlags |= script.VER_WITNESS | script.VER_NULLDUMMY
	}

	if !bl.Trusted {
		var blockTime uint32
		var had_witness bool

		if (bl.VerifyFlags & script.VER_CSV) != 0 {
			blockTime = bl.MedianPastTime
		} else {
			blockTime = bl.BlockTime()
		}

		// Verify merkle root of witness data
		if (bl.VerifyFlags & script.VER_WITNESS) != 0 {
			var i int
			for i = len(bl.Txs[0].TxOut) - 1; i >= 0; i-- {
				o := bl.Txs[0].TxOut[i]
				if len(o.Pk_script) >= 38 && bytes.Equal(o.Pk_script[:6], []byte{0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed}) {
					if len(bl.Txs[0].SegWit) != 1 || len(bl.Txs[0].SegWit[0]) != 1 || len(bl.Txs[0].SegWit[0][0]) != 32 {
						er = errors.New("CheckBlock() : invalid witness nonce size - RPC_Result:bad-witness-nonce-size")
						println(er.Error())
						println(bl.Hash.String(), len(bl.Txs[0].SegWit))
						return
					}

					// The malleation check is ignored; as the transaction tree itself
					// already does not permit it, it is impossible to trigger in the
					// witness tree.
					merkle, _ := btc.GetWitnessMerkle(bl.Txs)
					with_nonce := btc.Sha2Sum(append(merkle, bl.Txs[0].SegWit[0][0]...))

					if !bytes.Equal(with_nonce[:], o.Pk_script[6:38]) {
						er = errors.New("CheckBlock(): Witness Merkle mismatch - RPC_Result:bad-witness-merkle-match")
						return
					}

					had_witness = true
					break
				}
			}
		}

		if !had_witness {
			for _, t := range bl.Txs {
				if t.SegWit != nil {
					er = errors.New("CheckBlock(): unexpected witness data found - RPC_Result:unexpected-witness")
					return
				}
			}
		}

		// Check transactions - this is the most time consuming task
		if !CheckTransactions(bl.Txs, bl.Height, blockTime) {
			er = errors.New("CheckBlock() : CheckTransactions() failed - RPC_Result:bad-tx")
			return
		}
	}
	return
}
示例#8
0
func (ch *Chain) PreCheckBlock(bl *btc.Block) (er error, dos bool, maybelater bool) {
	// Size limits
	if len(bl.Raw) < 81 {
		er = errors.New("CheckBlock() : size limits failed - RPC_Result:bad-blk-length")
		dos = true
		return
	}

	if bl.Version() == 0 {
		er = errors.New("CheckBlock() : Block version 0 not allowed - RPC_Result:bad-version")
		dos = true
		return
	}

	// Check proof-of-work
	if !btc.CheckProofOfWork(bl.Hash, bl.Bits()) {
		er = errors.New("CheckBlock() : proof of work failed - RPC_Result:high-hash")
		dos = true
		return
	}

	// Check timestamp (must not be higher than now +2 hours)
	if int64(bl.BlockTime()) > time.Now().Unix()+2*60*60 {
		er = errors.New("CheckBlock() : block timestamp too far in the future - RPC_Result:time-too-new")
		dos = true
		return
	}

	if prv, pres := ch.BlockIndex[bl.Hash.BIdx()]; pres {
		if prv.Parent == nil {
			// This is genesis block
			er = errors.New("Genesis")
			return
		} else {
			er = errors.New("CheckBlock: " + bl.Hash.String() + " already in - RPC_Result:duplicate")
			return
		}
	}

	prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()]
	if !ok {
		er = errors.New("CheckBlock: " + bl.Hash.String() + " parent not found - RPC_Result:bad-prevblk")
		maybelater = true
		return
	}

	bl.Height = prevblk.Height + 1

	// Reject the block if it reaches into the chain deeper than our unwind buffer
	if prevblk != ch.BlockTreeEnd && int(ch.BlockTreeEnd.Height)-int(bl.Height) >= MovingCheckopintDepth {
		er = errors.New(fmt.Sprint("CheckBlock: btc.Block ", bl.Hash.String(),
			" hooks too deep into the chain: ", bl.Height, "/", ch.BlockTreeEnd.Height, " ",
			btc.NewUint256(bl.ParentHash()).String(), " - RPC_Result:bad-prevblk"))
		return
	}

	// Check proof of work
	gnwr := ch.GetNextWorkRequired(prevblk, bl.BlockTime())
	if bl.Bits() != gnwr {
		er = errors.New("CheckBlock: incorrect proof of work - RPC_Result:bad-diffbits")
		dos = true
		return
	}

	// Check timestamp against prev
	bl.MedianPastTime = prevblk.GetMedianTimePast()
	if bl.BlockTime() <= bl.MedianPastTime {
		er = errors.New("CheckBlock: block's timestamp is too early - RPC_Result:time-too-old")
		dos = true
		return
	}

	if bl.Version() < 2 && bl.Height >= ch.Consensus.BIP34Height ||
		bl.Version() < 3 && bl.Height >= ch.Consensus.BIP66Height ||
		bl.Version() < 4 && bl.Height >= ch.Consensus.BIP65Height {
		// bad block version
		erstr := fmt.Sprintf("0x%08x", bl.Version())
		er = errors.New("CheckBlock() : Rejected Version=" + erstr + " blolck - RPC_Result:bad-version(" + erstr + ")")
		dos = true
		return
	}
	return
}