// 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 CheckDoubleSpends( argTx *wire.MsgTx, txs []*wire.MsgTx) ([]*wire.ShaHash, error) { var dubs []*wire.ShaHash // slice of all double-spent txs argTxid := argTx.TxSha() for _, compTx := range txs { compTxid := compTx.TxSha() // check if entire tx is dup if argTxid.IsEqual(&compTxid) { return nil, fmt.Errorf("tx %s is dup", argTxid.String()) } // not dup, iterate through inputs of argTx for _, argIn := range argTx.TxIn { // iterate through inputs of compTx for _, compIn := range compTx.TxIn { if OutPointsEqual( argIn.PreviousOutPoint, compIn.PreviousOutPoint) { // found double spend dubs = append(dubs, &compTxid) break // back to argIn loop } } } } return dubs, nil }
// CalcPriority returns a transaction priority given a transaction and the sum // of each of its input values multiplied by their age (# of confirmations). // Thus, the final formula for the priority is: // sum(inputValue * inputAge) / adjustedTxSize func CalcPriority(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 { // In order to encourage spending multiple old unspent transaction // outputs thereby reducing the total set, don't count the constant // overhead for each input as well as enough bytes of the signature // script to cover a pay-to-script-hash redemption with a compressed // pubkey. This makes additional inputs free by boosting the priority // of the transaction accordingly. No more incentive is given to avoid // encouraging gaming future transactions through the use of junk // outputs. This is the same logic used in the reference // implementation. // // The constant overhead for a txin is 41 bytes since the previous // outpoint is 36 bytes + 4 bytes for the sequence + 1 byte the // signature script length. // // A compressed pubkey pay-to-script-hash redemption with a maximum len // signature is of the form: // [OP_DATA_73 <73-byte sig> + OP_DATA_35 + {OP_DATA_33 // <33 byte compresed pubkey> + OP_CHECKSIG}] // // Thus 1 + 73 + 1 + 1 + 33 + 1 = 110 overhead := 0 for _, txIn := range tx.TxIn { // Max inputs + size can't possibly overflow here. overhead += 41 + minInt(110, len(txIn.SignatureScript)) } serializedTxSize := tx.SerializeSize() if overhead >= serializedTxSize { return 0.0 } inputValueAge := calcInputValueAge(tx, utxoView, nextBlockHeight) return inputValueAge / float64(serializedTxSize-overhead) }
// dbFetchTx looks up the passed transaction hash in the transaction index and // loads it from the database. func dbFetchTx(dbTx database.Tx, hash *chainhash.Hash) (*wire.MsgTx, error) { // Look up the location of the transaction. blockRegion, err := dbFetchTxIndexEntry(dbTx, hash) if err != nil { return nil, err } if blockRegion == nil { return nil, fmt.Errorf("transaction %v not found", hash) } // Load the raw transaction bytes from the database. txBytes, err := dbTx.FetchBlockRegion(blockRegion) if err != nil { return nil, err } // Deserialize the transaction. var msgTx wire.MsgTx err = msgTx.Deserialize(bytes.NewReader(txBytes)) if err != nil { return nil, err } return &msgTx, nil }
// AddSigHashes computes, then adds the partial sighashes for the passed // transaction. func (h *HashCache) AddSigHashes(tx *wire.MsgTx) { h.Lock() defer h.Unlock() sigHashes := NewTxSigHashes(tx) txid := tx.TxHash() h.sigHashes[txid] = sigHashes return }
// TestCalcSignatureHash runs the Bitcoin Core signature hash calculation tests // in sighash.json. // https://github.com/bitcoin/bitcoin/blob/master/src/test/data/sighash.json func TestCalcSignatureHash(t *testing.T) { file, err := ioutil.ReadFile("data/sighash.json") if err != nil { t.Errorf("TestCalcSignatureHash: %v\n", err) return } var tests [][]interface{} err = json.Unmarshal(file, &tests) if err != nil { t.Errorf("TestCalcSignatureHash couldn't Unmarshal: %v\n", err) return } for i, test := range tests { if i == 0 { // Skip first line -- contains comments only. continue } if len(test) != 5 { t.Fatalf("TestCalcSignatureHash: Test #%d has "+ "wrong length.", i) } var tx wire.MsgTx rawTx, _ := hex.DecodeString(test[0].(string)) err := tx.Deserialize(bytes.NewReader(rawTx)) if err != nil { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Failed to parse transaction: %v", i, err) continue } subScript, _ := hex.DecodeString(test[1].(string)) parsedScript, err := parseScript(subScript) if err != nil { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Failed to parse sub-script: %v", i, err) continue } hashType := SigHashType(testVecF64ToUint32(test[3].(float64))) hash := calcSignatureHash(parsedScript, hashType, &tx, int(test[2].(float64))) expectedHash, _ := chainhash.NewHashFromStr(test[4].(string)) if !bytes.Equal(hash, expectedHash[:]) { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Signature hash mismatch.", i) } } }
func spendOutput(txHash *wire.ShaHash, index uint32, outputValues ...int64) *wire.MsgTx { tx := wire.MsgTx{ TxIn: []*wire.TxIn{ &wire.TxIn{ PreviousOutPoint: wire.OutPoint{Hash: *txHash, Index: index}, }, }, } for _, val := range outputValues { tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val}) } return &tx }
func newCoinBase(outputValues ...int64) *wire.MsgTx { tx := wire.MsgTx{ TxIn: []*wire.TxIn{ &wire.TxIn{ PreviousOutPoint: wire.OutPoint{Index: ^uint32(0)}, }, }, } for _, val := range outputValues { tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val}) } return &tx }
// receiverHtlcSpendRedeem constructs a valid witness allowing the receiver of // an HTLC to redeem the conditional payment in the event that their commitment // transaction is broadcast. Since this is a pay out to the receiving party as // an output on their commitment transaction, a relative time delay is required // before the output can be spent. func receiverHtlcSpendRedeem(commitScript []byte, outputAmt btcutil.Amount, reciverKey *btcec.PrivateKey, sweepTx *wire.MsgTx, paymentPreimage []byte, relativeTimeout uint32) (wire.TxWitness, error) { // In order to properly spend the transaction, we need to set the // sequence number. We do this by convering the relative block delay // into a sequence number value able to be interpeted by // OP_CHECKSEQUENCEVERIFY. sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, relativeTimeout) // Additionally, OP_CSV requires that the version of the transaction // spending a pkscript with OP_CSV within it *must* be >= 2. sweepTx.Version = 2 hashCache := txscript.NewTxSigHashes(sweepTx) sweepSig, err := txscript.RawTxInWitnessSignature( sweepTx, hashCache, 0, int64(outputAmt), commitScript, txscript.SigHashAll, reciverKey) if err != nil { return nil, err } // Place a one as the first item in the evaluated witness stack to // force script execution to the HTLC redemption clause. witnessStack := wire.TxWitness(make([][]byte, 4)) witnessStack[0] = sweepSig witnessStack[1] = paymentPreimage witnessStack[2] = []byte{1} witnessStack[3] = commitScript return witnessStack, nil }
// receiverHtlcSpendTimeout constructs a valid witness allowing the sender of // an HTLC to recover the pending funds after an absolute timeout in the // scenario that the receiver of the HTLC broadcasts their version of the // commitment transaction. func receiverHtlcSpendTimeout(commitScript []byte, outputAmt btcutil.Amount, senderKey *btcec.PrivateKey, sweepTx *wire.MsgTx, absoluteTimeout uint32) (wire.TxWitness, error) { // The HTLC output has an absolute time period before we are permitted // to recover the pending funds. Therefore we need to set the locktime // on this sweeping transaction in order to pass Script verification. sweepTx.LockTime = absoluteTimeout hashCache := txscript.NewTxSigHashes(sweepTx) sweepSig, err := txscript.RawTxInWitnessSignature( sweepTx, hashCache, 0, int64(outputAmt), commitScript, txscript.SigHashAll, senderKey) if err != nil { return nil, err } witnessStack := wire.TxWitness(make([][]byte, 4)) witnessStack[0] = sweepSig witnessStack[1] = []byte{0} witnessStack[2] = []byte{0} witnessStack[3] = commitScript return witnessStack, nil }
func equalTxs(t *testing.T, got, exp *wire.MsgTx) { var bufGot, bufExp bytes.Buffer err := got.Serialize(&bufGot) if err != nil { t.Fatal(err) } err = exp.Serialize(&bufExp) if err != nil { t.Fatal(err) } if !bytes.Equal(bufGot.Bytes(), bufExp.Bytes()) { t.Errorf("Found unexpected wire.MsgTx:") t.Errorf("Got: %v", got) t.Errorf("Expected: %v", exp) } }
// BUGS: // - The transaction is not inspected to be relevant before publishing using // sendrawtransaction, so connection errors to btcd could result in the tx // never being added to the wallet database. // - Once the above bug is fixed, wallet will require a way to purge invalid // transactions from the database when they are rejected by the network, other // than double spending them. func (s *walletServer) PublishTransaction(ctx context.Context, req *pb.PublishTransactionRequest) ( *pb.PublishTransactionResponse, error) { var msgTx wire.MsgTx err := msgTx.Deserialize(bytes.NewReader(req.SignedTransaction)) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Bytes do not represent a valid raw transaction: %v", err) } err = s.wallet.PublishTransaction(&msgTx) if err != nil { return nil, translateError(err) } return &pb.PublishTransactionResponse{}, nil }
// TxToString prints out some info about a transaction. for testing / debugging func TxToString(tx *wire.MsgTx) string { str := fmt.Sprintf("size %d vsize %d wsize %d locktime %d txid %s\n", tx.SerializeSize(), blockchain.GetTxVirtualSize(btcutil.NewTx(tx)), tx.SerializeSize(), tx.LockTime, tx.TxSha().String()) for i, in := range tx.TxIn { str += fmt.Sprintf("Input %d spends %s\n", i, in.PreviousOutPoint.String()) str += fmt.Sprintf("\tSigScript: %x\n", in.SignatureScript) for j, wit := range in.Witness { str += fmt.Sprintf("\twitness %d: %x\n", j, wit) } } for i, out := range tx.TxOut { if out != nil { str += fmt.Sprintf("output %d script: %x amt: %d\n", i, out.PkScript, out.Value) } else { str += fmt.Sprintf("output %d nil (WARNING)\n", i) } } return str }
func (s *SPVCon) NewOutgoingTx(tx *wire.MsgTx) error { txid := tx.TxSha() // assign height of zero for txs we create err := s.TS.AddTxid(&txid, 0) if err != nil { return err } _, err = s.TS.Ingest(tx, 0) // our own tx; don't keep track of false positives if err != nil { return err } // make an inv message instead of a tx message to be polite iv1 := wire.NewInvVect(wire.InvTypeWitnessTx, &txid) invMsg := wire.NewMsgInv() err = invMsg.AddInvVect(iv1) if err != nil { return err } s.outMsgQueue <- invMsg return nil }
// htlcSpendTimeout constructs a valid witness allowing the sender of an HTLC // to recover the pending funds after an absolute, then relative locktime // period. func senderHtlcSpendTimeout(commitScript []byte, outputAmt btcutil.Amount, senderKey *btcec.PrivateKey, sweepTx *wire.MsgTx, absoluteTimeout, relativeTimeout uint32) (wire.TxWitness, error) { // Since the HTLC output has an absolute timeout before we're permitted // to sweep the output, we need to set the locktime of this sweepign // transaction to that aboslute value in order to pass Script // verification. sweepTx.LockTime = absoluteTimeout // Additionally, we're required to wait a relative period of time // before we can sweep the output in order to allow the other party to // contest our claim of validity to this version of the commitment // transaction. sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, relativeTimeout) // Finally, OP_CSV requires that the version of the transaction // spending a pkscript with OP_CSV within it *must* be >= 2. sweepTx.Version = 2 hashCache := txscript.NewTxSigHashes(sweepTx) sweepSig, err := txscript.RawTxInWitnessSignature( sweepTx, hashCache, 0, int64(outputAmt), commitScript, txscript.SigHashAll, senderKey) if err != nil { return nil, err } // We place a zero as the first item of the evaluated witness stack in // order to force Script execution to the HTLC timeout clause. witnessStack := wire.TxWitness(make([][]byte, 3)) witnessStack[0] = sweepSig witnessStack[1] = []byte{0} witnessStack[2] = commitScript return witnessStack, nil }
// EstFee gives a fee estimate based on a tx and a sat/Byte target. // The TX should have all outputs, including the change address already // populated (with potentially 0 amount. Also it should have all inputs // populated, but inputs don't need to have sigscripts or witnesses // (it'll guess the sizes of sigs/wits that arent' filled in). func EstFee(otx *wire.MsgTx, spB int64) int64 { mtsig := make([]byte, 72) mtpub := make([]byte, 33) tx := otx.Copy() // iterate through txins, replacing subscript sigscripts with noise // sigs or witnesses for _, txin := range tx.TxIn { // check wpkh if len(txin.SignatureScript) == 22 && txin.SignatureScript[0] == 0x00 && txin.SignatureScript[1] == 0x14 { txin.SignatureScript = nil txin.Witness = make([][]byte, 2) txin.Witness[0] = mtsig txin.Witness[1] = mtpub } else if len(txin.SignatureScript) == 34 && txin.SignatureScript[0] == 0x00 && txin.SignatureScript[1] == 0x20 { // p2wsh -- sig lenght is a total guess! txin.SignatureScript = nil txin.Witness = make([][]byte, 3) // 3 sigs? totally guessing here txin.Witness[0] = mtsig txin.Witness[1] = mtsig txin.Witness[2] = mtsig } else { // assume everything else is p2pkh. Even though it's not txin.Witness = nil txin.SignatureScript = make([]byte, 105) // len of p2pkh sigscript } } fmt.Printf(TxToString(tx)) size := int64(blockchain.GetTxVirtualSize(btcutil.NewTx(tx))) fmt.Printf("%d spB, est vsize %d, fee %d\n", spB, size, size*spB) return size * spB }
// TxHandler takes in transaction messages that come in from either a request // after an inv message or after a merkle block message. func (s *SPVCon) TxHandler(m *wire.MsgTx) { s.TS.OKMutex.Lock() height, ok := s.TS.OKTxids[m.TxSha()] s.TS.OKMutex.Unlock() if !ok { log.Printf("Tx %s unknown, will not ingest\n", m.TxSha().String()) 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()) // } // } utilTx := btcutil.NewTx(m) if !s.HardMode || s.TS.localFilter.MatchTxAndUpdate(utilTx) { hits, err := s.TS.Ingest(m, height) if err != nil { log.Printf("Incoming Tx error: %s\n", err.Error()) return } if hits == 0 && !s.HardMode { log.Printf("tx %s had no hits, filter false positive.", m.TxSha().String()) s.fPositives <- 1 // add one false positive to chan return } log.Printf("tx %s ingested and matches %d utxo/adrs.", m.TxSha().String(), hits) } }
// BUGS: // - InputIndexes request field is ignored. func (s *walletServer) SignTransaction(ctx context.Context, req *pb.SignTransactionRequest) ( *pb.SignTransactionResponse, error) { defer zero.Bytes(req.Passphrase) var tx wire.MsgTx err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction)) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Bytes do not represent a valid raw transaction: %v", err) } lock := make(chan time.Time, 1) defer func() { lock <- time.Time{} // send matters, not the value }() err = s.wallet.Unlock(req.Passphrase, lock) if err != nil { return nil, translateError(err) } invalidSigs, err := s.wallet.SignTransaction(&tx, txscript.SigHashAll, nil, nil, nil) if err != nil { return nil, translateError(err) } invalidInputIndexes := make([]uint32, len(invalidSigs)) for i, e := range invalidSigs { invalidInputIndexes[i] = e.InputIndex } var serializedTransaction bytes.Buffer serializedTransaction.Grow(tx.SerializeSize()) err = tx.Serialize(&serializedTransaction) if err != nil { return nil, translateError(err) } resp := &pb.SignTransactionResponse{ Transaction: serializedTransaction.Bytes(), UnsignedInputIndexes: invalidInputIndexes, } return resp, nil }
// NewTxRecordFromMsgTx creates a new transaction record that may be inserted // into the store. func NewTxRecordFromMsgTx(msgTx *wire.MsgTx, received time.Time) (*TxRecord, error) { buf := bytes.NewBuffer(make([]byte, 0, msgTx.SerializeSize())) err := msgTx.Serialize(buf) if err != nil { str := "failed to serialize transaction" return nil, storeError(ErrInput, str, err) } rec := &TxRecord{ MsgTx: *msgTx, Received: received, SerializedTx: buf.Bytes(), Hash: msgTx.TxSha(), } return rec, nil }
// Ingest puts a tx into the DB atomically. This can result in a // gain, a loss, or no result. Gain or loss in satoshis is returned. func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) { var hits uint32 var err error var nUtxoBytes [][]byte // tx has been OK'd by SPV; check tx sanity utilTx := btcutil.NewTx(tx) // convert for validation // checks basic stuff like there are inputs and ouputs err = blockchain.CheckTransactionSanity(utilTx) if err != nil { return hits, err } // note that you can't check signatures; this is SPV. // 0 conf SPV means pretty much nothing. Anyone can say anything. spentOPs := make([][]byte, len(tx.TxIn)) // before entering into db, serialize all inputs of the ingested tx for i, txin := range tx.TxIn { spentOPs[i], err = outPointToBytes(&txin.PreviousOutPoint) if err != nil { return hits, err } } // go through txouts, and then go through addresses to match // generate PKscripts for all addresses wPKscripts := make([][]byte, len(ts.Adrs)) aPKscripts := make([][]byte, len(ts.Adrs)) for i, _ := range ts.Adrs { // iterate through all our addresses // convert regular address to witness address. (split adrs later) wa, err := btcutil.NewAddressWitnessPubKeyHash( ts.Adrs[i].PkhAdr.ScriptAddress(), ts.Param) if err != nil { return hits, err } wPKscripts[i], err = txscript.PayToAddrScript(wa) if err != nil { return hits, err } aPKscripts[i], err = txscript.PayToAddrScript(ts.Adrs[i].PkhAdr) if err != nil { return hits, err } } cachedSha := tx.TxSha() // iterate through all outputs of this tx, see if we gain for i, out := range tx.TxOut { for j, ascr := range aPKscripts { // detect p2wpkh witBool := false if bytes.Equal(out.PkScript, wPKscripts[j]) { witBool = true } if bytes.Equal(out.PkScript, ascr) || witBool { // new utxo found var newu Utxo // create new utxo and copy into it newu.AtHeight = height newu.KeyIdx = ts.Adrs[j].KeyIdx newu.Value = out.Value newu.IsWit = witBool // copy witness version from pkscript var newop wire.OutPoint newop.Hash = cachedSha newop.Index = uint32(i) newu.Op = newop b, err := newu.ToBytes() if err != nil { return hits, err } nUtxoBytes = append(nUtxoBytes, b) hits++ break // txos can match only 1 script } } } err = ts.StateDB.Update(func(btx *bolt.Tx) error { // get all 4 buckets duf := btx.Bucket(BKTUtxos) // sta := btx.Bucket(BKTState) old := btx.Bucket(BKTStxos) txns := btx.Bucket(BKTTxns) if duf == nil || old == nil || txns == nil { return fmt.Errorf("error: db not initialized") } // iterate through duffel bag and look for matches // this makes us lose money, which is regrettable, but we need to know. for _, nOP := range spentOPs { v := duf.Get(nOP) if v != nil { hits++ // do all this just to figure out value we lost x := make([]byte, len(nOP)+len(v)) copy(x, nOP) copy(x[len(nOP):], v) lostTxo, err := UtxoFromBytes(x) if err != nil { return err } // after marking for deletion, save stxo to old bucket var st Stxo // generate spent txo st.Utxo = lostTxo // assign outpoint st.SpendHeight = height // spent at height st.SpendTxid = cachedSha // spent by txid stxb, err := st.ToBytes() // serialize if err != nil { return err } err = old.Put(nOP, stxb) // write nOP:v outpoint:stxo bytes if err != nil { return err } err = duf.Delete(nOP) if err != nil { return err } } } // done losing utxos, next gain utxos // next add all new utxos to db, this is quick as the work is above for _, ub := range nUtxoBytes { err = duf.Put(ub[:36], ub[36:]) if err != nil { return err } } // if hits is nonzero it's a relevant tx and we should store it var buf bytes.Buffer tx.Serialize(&buf) err = txns.Put(cachedSha.Bytes(), buf.Bytes()) if err != nil { return err } return nil }) return hits, err }
// fundTx attempts to fund a transaction sending amt bitcoin. The coins are // selected such that the final amount spent pays enough fees as dictated by // the passed fee rate. The passed fee rate should be expressed in // satoshis-per-byte. // // NOTE: The memWallet's mutex must be held when this function is called. func (m *memWallet) fundTx(tx *wire.MsgTx, amt btcutil.Amount, feeRate btcutil.Amount) error { const ( // spendSize is the largest number of bytes of a sigScript // which spends a p2pkh output: OP_DATA_73 <sig> OP_DATA_33 <pubkey> spendSize = 1 + 73 + 1 + 33 ) var ( amtSelected btcutil.Amount txSize int ) for outPoint, utxo := range m.utxos { // Skip any outputs that are still currently immature or are // currently locked. if !utxo.isMature(m.currentHeight) || utxo.isLocked { continue } amtSelected += utxo.value // Add the selected output to the transaction, updating the // current tx size while accounting for the size of the future // sigScript. tx.AddTxIn(wire.NewTxIn(&outPoint, nil, nil)) txSize = tx.SerializeSize() + spendSize*len(tx.TxIn) // Calculate the fee required for the txn at this point // observing the specified fee rate. If we don't have enough // coins from he current amount selected to pay the fee, then // continue to grab more coins. reqFee := btcutil.Amount(txSize * int(feeRate)) if amtSelected-reqFee < amt { continue } // If we have any change left over, then add an additional // output to the transaction reserved for change. changeVal := amtSelected - amt - reqFee if changeVal > 0 { addr, err := m.newAddress() if err != nil { return err } pkScript, err := txscript.PayToAddrScript(addr) if err != nil { return err } changeOutput := &wire.TxOut{ Value: int64(changeVal), PkScript: pkScript, } tx.AddTxOut(changeOutput) } return nil } // If we've reached this point, then coin selection failed due to an // insufficient amount of coins. return fmt.Errorf("not enough funds for coin selection") }