Пример #1
0
// extracts the counts of the standard outscripts each transaction contains
func ExtractOutScripts(tx *btcwire.MsgTx) map[btcscript.ScriptClass]int {
	outmap := make(map[btcscript.ScriptClass]int)
	for _, txout := range tx.TxOut {
		class := btcscript.GetScriptClass(txout.PkScript)
		outmap[class]++
	}
	return outmap
}
Пример #2
0
// Only log transactions that are not of type "pubkeyhash"
func filter(tx *btcwire.MsgTx) bool {
	for _, txout := range tx.TxOut {
		script := txout.PkScript
		scriptclass := btcscript.GetScriptClass(script).String()
		if scriptclass != "pubkeyhash" {
			return true
		}
	}
	return true
}
Пример #3
0
// isNonstandardTransaction determines whether a transaction contains any
// scripts which are not one of the standard types.
func isNonstandardTransaction(tx *btcutil.Tx) bool {
	// TODO(davec): Should there be checks for the input signature scripts?

	// Check all of the output public key scripts for non-standard scripts.
	for _, txOut := range tx.MsgTx().TxOut {
		scriptClass := btcscript.GetScriptClass(txOut.PkScript)
		if scriptClass == btcscript.NonStandardTy {
			return true
		}
	}
	return false
}
Пример #4
0
// maybeAddOutpoint potentially adds the passed outpoint to the bloom filter
// depending on the bloom update flags and the type of the passed public key
// script.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) maybeAddOutpoint(pkScript []byte, outHash *btcwire.ShaHash, outIdx uint32) {
	switch bf.msgFilterLoad.Flags {
	case btcwire.BloomUpdateAll:
		outpoint := btcwire.NewOutPoint(outHash, outIdx)
		bf.addOutPoint(outpoint)
	case btcwire.BloomUpdateP2PubkeyOnly:
		class := btcscript.GetScriptClass(pkScript)
		if class == btcscript.PubKeyTy || class == btcscript.MultiSigTy {
			outpoint := btcwire.NewOutPoint(outHash, outIdx)
			bf.addOutPoint(outpoint)
		}
	}
}
Пример #5
0
// checkPkScriptStandard performs a series of checks on a transaction ouput
// script (public key script) to ensure it is a "standard" public key script.
// A standard public key script is one that is a recognized form, and for
// multi-signature scripts, only contains from 1 to 3 signatures.
func checkPkScriptStandard(pkScript []byte) error {
	scriptClass := btcscript.GetScriptClass(pkScript)
	switch scriptClass {
	case btcscript.MultiSigTy:
		// TODO(davec): Need to get the actual number of signatures.
		numSigs := 1
		if numSigs < 1 {
			str := fmt.Sprintf("multi-signature script with no " +
				"signatures")
			return TxRuleError(str)
		}
		if numSigs > maxStandardMultiSigs {
			str := fmt.Sprintf("multi-signature script with %d "+
				"signatures which is more than the allowed max "+
				"of %d", numSigs, maxStandardMultiSigs)
			return TxRuleError(str)
		}

	case btcscript.NonStandardTy:
		return TxRuleError(fmt.Sprintf("non-standard script form"))
	}

	return nil
}
Пример #6
0
// checkTransactionStandard performs a series of checks on a transaction to
// ensure it is a "standard" transaction.  A standard transaction is one that
// conforms to several additional limiting cases over what is considered a
// "sane" transaction such as having a version in the supported range, being
// finalized, conforming to more stringent size constraints, having scripts
// of recognized forms, and not containing "dust" outputs (those that are
// so small it costs more to process them than they are worth).
func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
	msgTx := tx.MsgTx()

	// The transaction must be a currently supported version.
	if msgTx.Version > btcwire.TxVersion || msgTx.Version < 1 {
		str := fmt.Sprintf("transaction version %d is not in the "+
			"valid range of %d-%d", msgTx.Version, 1,
			btcwire.TxVersion)
		return TxRuleError(str)
	}

	// The transaction must be finalized to be standard and therefore
	// considered for inclusion in a block.
	if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) {
		return TxRuleError("transaction is not finalized")
	}

	// Since extremely large transactions with a lot of inputs can cost
	// almost as much to process as the sender fees, limit the maximum
	// size of a transaction.  This also helps mitigate CPU exhaustion
	// attacks.
	serializedLen := msgTx.SerializeSize()
	if serializedLen > maxStandardTxSize {
		str := fmt.Sprintf("transaction size of %v is larger than max "+
			"allowed size of %v", serializedLen, maxStandardTxSize)
		return TxRuleError(str)
	}

	for i, txIn := range msgTx.TxIn {
		// Each transaction input signature script must not exceed the
		// maximum size allowed for a standard transaction.  See
		// the comment on maxStandardSigScriptSize for more details.
		sigScriptLen := len(txIn.SignatureScript)
		if sigScriptLen > maxStandardSigScriptSize {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script size of %d bytes is large than max "+
				"allowed size of %d bytes", i, sigScriptLen,
				maxStandardSigScriptSize)
			return TxRuleError(str)
		}

		// Each transaction input signature script must only contain
		// opcodes which push data onto the stack.
		if !btcscript.IsPushOnlyScript(txIn.SignatureScript) {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script is not push only", i)
			return TxRuleError(str)
		}

		// Each transaction input signature script must only contain
		// canonical data pushes.  A canonical data push is one where
		// the minimum possible number of bytes is used to represent
		// the data push as possible.
		if !btcscript.HasCanonicalPushes(txIn.SignatureScript) {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script has a non-canonical data push", i)
			return TxRuleError(str)
		}
	}

	// None of the output public key scripts can be a non-standard script or
	// be "dust".
	numNullDataOutputs := 0
	for i, txOut := range msgTx.TxOut {
		scriptClass := btcscript.GetScriptClass(txOut.PkScript)
		err := checkPkScriptStandard(txOut.PkScript, scriptClass)
		if err != nil {
			str := fmt.Sprintf("transaction output %d: %v", i, err)
			return TxRuleError(str)
		}

		// Accumulate the number of outputs which only carry data.
		if scriptClass == btcscript.NullDataTy {
			numNullDataOutputs++
		}

		if isDust(txOut) {
			str := fmt.Sprintf("transaction output %d: payment "+
				"of %d is dust", i, txOut.Value)
			return TxRuleError(str)
		}
	}

	// A standard transaction must not have more than one output script that
	// only carries data.
	if numNullDataOutputs > 1 {
		return TxRuleError("more than one transaction output is a " +
			"nulldata script")
	}

	return nil
}
Пример #7
0
// txToPairs creates a raw transaction sending the amounts for each
// address/amount pair and fee to each address and the miner.  minconf
// specifies the minimum number of confirmations required before an
// unspent output is eligible for spending. Leftover input funds not sent
// to addr or as a fee for the miner are sent to a newly generated
// address. If change is needed to return funds back to an owned
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed
// block hash) Utxo.  ErrInsufficientFunds is returned if there are not
// enough eligible unspent outputs to create the transaction.
func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
	minconf int) (*CreatedTx, error) {

	// Wallet must be unlocked to compose transaction.
	if a.IsLocked() {
		return nil, wallet.ErrWalletLocked
	}

	// Create a new transaction which will include all input scripts.
	msgtx := btcwire.NewMsgTx()

	// Calculate minimum amount needed for inputs.
	var amt btcutil.Amount
	for _, v := range pairs {
		// Error out if any amount is negative.
		if v <= 0 {
			return nil, ErrNonPositiveAmount
		}
		amt += v
	}

	// Add outputs to new tx.
	for addrStr, amt := range pairs {
		addr, err := btcutil.DecodeAddress(addrStr, activeNet.Params)
		if err != nil {
			return nil, fmt.Errorf("cannot decode address: %s", err)
		}

		// Add output to spend amt to addr.
		pkScript, err := btcscript.PayToAddrScript(addr)
		if err != nil {
			return nil, fmt.Errorf("cannot create txout script: %s", err)
		}
		txout := btcwire.NewTxOut(int64(amt), pkScript)
		msgtx.AddTxOut(txout)
	}

	// Get current block's height and hash.
	bs, err := GetCurBlock()
	if err != nil {
		return nil, err
	}

	// Make a copy of msgtx before any inputs are added.  This will be
	// used as a starting point when trying a fee and starting over with
	// a higher fee if not enough was originally chosen.
	txNoInputs := msgtx.Copy()

	unspent, err := a.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}

	// Filter out unspendable outputs, that is, remove those that (at this
	// time) are not P2PKH outputs.  Other inputs must be manually included
	// in transactions and sent (for example, using createrawtransaction,
	// signrawtransaction, and sendrawtransaction).
	eligible := make([]txstore.Credit, 0, len(unspent))
	for i := range unspent {
		switch btcscript.GetScriptClass(unspent[i].TxOut().PkScript) {
		case btcscript.PubKeyHashTy:
			if !unspent[i].Confirmed(minconf, bs.Height) {
				continue
			}
			// Coinbase transactions must have have reached maturity
			// before their outputs may be spent.
			if unspent[i].IsCoinbase() {
				target := btcchain.CoinbaseMaturity
				if !unspent[i].Confirmed(target, bs.Height) {
					continue
				}
			}

			// Locked unspent outputs are skipped.
			if a.LockedOutpoint(*unspent[i].OutPoint()) {
				continue
			}

			eligible = append(eligible, unspent[i])
		}
	}

	// Sort eligible inputs, as selectInputs expects these to be sorted
	// by amount in reverse order.
	sort.Sort(sort.Reverse(ByAmount(eligible)))

	var selectedInputs []txstore.Credit
	// changeAddr is nil/zeroed until a change address is needed, and reused
	// again in case a change utxo has already been chosen.
	var changeAddr btcutil.Address

	// Get the number of satoshis to increment fee by when searching for
	// the minimum tx fee needed.
	fee := btcutil.Amount(0)
	for {
		msgtx = txNoInputs.Copy()

		// Select eligible outputs to be used in transaction based on the amount
		// neededing to sent, and the current fee estimation.
		inputs, btcin, err := selectInputs(eligible, amt, fee, minconf)
		if err != nil {
			return nil, err
		}

		// Check if there are leftover unspent outputs, and return coins back to
		// a new address we own.
		change := btcin - amt - fee
		if change > 0 {
			// Get a new change address if one has not already been found.
			if changeAddr == nil {
				changeAddr, err = a.ChangeAddress(&bs, cfg.KeypoolSize)
				if err != nil {
					return nil, fmt.Errorf("failed to get next address: %s", err)
				}

				// Mark change address as belonging to this account.
				AcctMgr.MarkAddressForAccount(changeAddr, a)
			}

			// Spend change.
			pkScript, err := btcscript.PayToAddrScript(changeAddr)
			if err != nil {
				return nil, fmt.Errorf("cannot create txout script: %s", err)
			}
			msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript))

			// Randomize index of the change output.
			rng := badrand.New(badrand.NewSource(time.Now().UnixNano()))
			r := rng.Int31n(int32(len(msgtx.TxOut))) // random index
			c := len(msgtx.TxOut) - 1                // change index
			msgtx.TxOut[r], msgtx.TxOut[c] = msgtx.TxOut[c], msgtx.TxOut[r]
		}

		// Selected unspent outputs become new transaction's inputs.
		for _, ip := range inputs {
			msgtx.AddTxIn(btcwire.NewTxIn(ip.OutPoint(), nil))
		}
		for i, input := range inputs {
			// Errors don't matter here, as we only consider the
			// case where len(addrs) == 1.
			_, addrs, _, _ := input.Addresses(activeNet.Params)
			if len(addrs) != 1 {
				continue
			}
			apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
			if !ok {
				continue // don't handle inputs to this yes
			}

			ai, err := a.Address(apkh)
			if err != nil {
				return nil, fmt.Errorf("cannot get address info: %v", err)
			}

			pka := ai.(wallet.PubKeyAddress)

			privkey, err := pka.PrivKey()
			if err == wallet.ErrWalletLocked {
				return nil, wallet.ErrWalletLocked
			} else if err != nil {
				return nil, fmt.Errorf("cannot get address key: %v", err)
			}

			sigscript, err := btcscript.SignatureScript(msgtx, i,
				input.TxOut().PkScript, btcscript.SigHashAll, privkey,
				ai.Compressed())
			if err != nil {
				return nil, fmt.Errorf("cannot create sigscript: %s", err)
			}
			msgtx.TxIn[i].SignatureScript = sigscript
		}

		noFeeAllowed := false
		if !cfg.DisallowFree {
			noFeeAllowed = allowFree(bs.Height, inputs, msgtx.SerializeSize())
		}
		if minFee := minimumFee(msgtx, noFeeAllowed); fee < minFee {
			fee = minFee
		} else {
			selectedInputs = inputs
			break
		}
	}

	// Validate msgtx before returning the raw transaction.
	flags := btcscript.ScriptCanonicalSignatures
	bip16 := time.Now().After(btcscript.Bip16Activation)
	if bip16 {
		flags |= btcscript.ScriptBip16
	}
	for i, txin := range msgtx.TxIn {
		engine, err := btcscript.NewScript(txin.SignatureScript,
			selectedInputs[i].TxOut().PkScript, i, msgtx, flags)
		if err != nil {
			return nil, fmt.Errorf("cannot create script engine: %s", err)
		}
		if err = engine.Execute(); err != nil {
			return nil, fmt.Errorf("cannot validate transaction: %s", err)
		}
	}

	buf := bytes.Buffer{}
	buf.Grow(msgtx.SerializeSize())
	if err := msgtx.BtcEncode(&buf, btcwire.ProtocolVersion); err != nil {
		// Hitting OOM by growing or writing to a bytes.Buffer already
		// panics, and all returned errors are unexpected.
		panic(err)
	}
	info := &CreatedTx{
		tx:         btcutil.NewTx(msgtx),
		inputs:     selectedInputs,
		changeAddr: changeAddr,
	}
	return info, nil
}
Пример #8
0
func main() {
	var (
		dataDir = flag.String("datadir", filepath.Join(btcutil.AppDataDir("btcd", false), "data"), "BTCD: Data directory")
		dbType  = flag.String("dbtype", "leveldb", "BTCD: Database backend")
	)
	flag.Parse()

	db, err := btcdbSetup(*dataDir, *dbType)
	if err != nil {
		log.Println("btcdbSetup error:", err)
		return
	}
	defer db.Close()

	var jsonFile = flag.String("json", "blockchainr.json", "blockchainr output")
	flag.Parse()

	blockchainrFile, err := ioutil.ReadFile(*jsonFile)
	if err != nil {
		log.Println("failed to read blockchainr.json:", err)
		return
	}

	results := make(map[string][]*inData)
	err = json.Unmarshal(blockchainrFile, &results)
	if err != nil {
		log.Println("Unmarshal error:", err)
		return
	}

	fmt.Println("blkH\tblkSha\tblkTime\ttxIndex\ttxSha\ttxInIndex\tprevBlkH\tprevBlkSha\tprevBlkTime\tr\taddr\twif")

	targets := make(map[[2]string][]*rData)

	for r, inDataList := range results {
		for _, in := range inDataList {
			rd := &rData{r: r, in: in}

			if err := fetch(db, rd); err != nil {
				log.Println("Skipping at fetch:", err)
				printLine(rd)
				continue
			}

			switch t := btcscript.GetScriptClass(rd.txPrevOut.PkScript); t {
			case btcscript.PubKeyHashTy:
				if err := processPubKeyHash(db, rd); err != nil {
					log.Println("Skipping at opCheckSig:", err)
					printLine(rd)
					continue
				}
			default:
				log.Println("Unsupported pkScript type:",
					btcscript.ScriptClassToName[t], rd.in)
				printLine(rd)
				continue
			}

			// TODO: group compressed and uncompressed together
			key := [...]string{rd.address, rd.r}
			targets[key] = append(targets[key], rd)
		}
	}

	// Do the magic!
	for _, target := range targets {
		if len(target) < 2 {
			// The r value was reused across different addresses
			// TODO: also this information would be interesting to graph

			for _, rd := range target {
				printLine(rd)
			}

			continue
		}

		a := target[0]
		b := target[1]

		log.Printf("[%v]\n", a.address)
		log.Printf("Repeated r value: %v (%v times)\n", a.r, len(target))

		privKey := recoverKey(a.signature, b.signature, a.hash, b.hash, a.pubKey)
		if privKey == nil {
			log.Print("recoverKey error\n\n")
			continue
		}

		wif, err := btcutil.NewWIF(privKey, &btcnet.MainNetParams, a.compressed)
		if err != nil {
			log.Printf("NewWIF error: %v\n\n", err)
			continue
		}

		for _, rd := range target {
			rd.wif = wif
			printLine(rd)
		}

		log.Printf("%v\n\n", wif.String())
	}
}