// 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) }
// broadcastTx tries to send the transaction using an api that will broadcast // a submitted transaction on behalf of the user. // // The transaction is broadcast to the bitcoin network using this API: // https://github.com/bitpay/insight-api // func broadcastTx(tx *wire.MsgTx) { buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) tx.Serialize(buf) hexstr := hex.EncodeToString(buf.Bytes()) url := "https://insight.bitpay.com/api/tx/send" contentType := "application/json" fmt.Printf("Sending transaction to: %s\n", url) sendTxJson := &sendTxJson{RawTx: hexstr} j, err := json.Marshal(sendTxJson) if err != nil { log.Fatal(fmt.Errorf("Broadcasting the tx failed: %v", err)) } buf = bytes.NewBuffer(j) resp, err := http.Post(url, contentType, buf) if err != nil { log.Fatal(fmt.Errorf("Broadcasting the tx failed: %v", err)) } b, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("The sending api responded with:\n%s\n", b) }
// dumpHex dumps the raw bytes of a Bitcoin transaction to stdout. This is the // format that Bitcoin wire's protocol accepts, so you could connect to a node, // send them these bytes, and if the tx was valid, the node would forward the // tx through the network. func dumpHex(tx *wire.MsgTx) { buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) tx.Serialize(buf) hexstr := hex.EncodeToString(buf.Bytes()) fmt.Println("Here is your raw bitcoin transaction:") fmt.Println(hexstr) }
// SignRawTransaction2Async returns an instance of a type that can be used to // get the result of the RPC at some future time by invoking the Receive // function on the returned instance. // // See SignRawTransaction2 for the blocking version and more details. func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(buf); err != nil { return newFutureError(err) } txHex = hex.EncodeToString(buf.Bytes()) } cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, nil, nil) return c.sendCmd(cmd) }
// SendRawTransactionAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See SendRawTransaction for the blocking version and more details. func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) if err := tx.Serialize(buf); err != nil { return newFutureError(err) } txHex = hex.EncodeToString(buf.Bytes()) } cmd := btcjson.NewSendRawTransactionCmd(txHex, &allowHighFees) return c.sendCmd(cmd) }
// 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(), } copy(rec.Hash[:], wire.DoubleSha256(rec.SerializedTx)) return rec, nil }
// 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 }
// 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)) 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") }