// 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) }
// 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 }
// fetchTxDataByLoc returns several pieces of data regarding the given tx // located by the block/offset/size location func (db *LevelDb) fetchTxDataByLoc(blkHeight int32, txOff int, txLen int, txspent []byte) (rtx *wire.MsgTx, rblksha *wire.ShaHash, rheight int32, rtxspent []byte, err error) { var blksha *wire.ShaHash var blkbuf []byte blksha, blkbuf, err = db.getBlkByHeight(blkHeight) if err != nil { if err == leveldb.ErrNotFound { err = database.ErrTxShaMissing } return } //log.Trace("transaction %v is at block %v %v txoff %v, txlen %v\n", // txsha, blksha, blkHeight, txOff, txLen) if len(blkbuf) < txOff+txLen { err = database.ErrTxShaMissing return } rbuf := bytes.NewReader(blkbuf[txOff : txOff+txLen]) var tx wire.MsgTx err = tx.Deserialize(rbuf) if err != nil { log.Warnf("unable to decode tx block %v %v txoff %v txlen %v", blkHeight, blksha, txOff, txLen) return } return &tx, blksha, blkHeight, txspent, nil }
// 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) }
// AbsorbTx absorbs money into wallet from a tx func (t *TxStore) AbsorbTx(tx *wire.MsgTx) error { if tx == nil { return fmt.Errorf("Tried to add nil tx") } var hits uint32 var acq int64 // check if any of the tx's outputs match my adrs for i, out := range tx.TxOut { // in each output of tx for _, a := range t.Adrs { // compare to each adr we have // more correct would be to check for full script // contains could have false positive? (p2sh/p2pkh same hash ..?) if bytes.Contains(out.PkScript, a.ScriptAddress()) { // hit hits++ acq += out.Value var newu Utxo newu.KeyIdx = a.KeyIdx newu.Txo = *out var newop wire.OutPoint newop.Hash = tx.TxSha() newop.Index = uint32(i) newu.Op = newop t.Utxos = append(t.Utxos, newu) break } } } log.Printf("%d hits, acquired %d", hits, acq) t.Sum += acq return nil }
// dbFetchTx looks up the passed transaction hash in the transaction index and // loads it from the database. func dbFetchTx(dbTx database.Tx, hash *wire.ShaHash) (*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 }
// 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) }
// Receive waits for the response promised by the future and returns the // found raw transactions. func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal as an array of strings. var searchRawTxnsResult []string err = json.Unmarshal(res, &searchRawTxnsResult) if err != nil { return nil, err } // Decode and deserialize each transaction. msgTxns := make([]*wire.MsgTx, 0, len(searchRawTxnsResult)) for _, hexTx := range searchRawTxnsResult { // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(hexTx) if err != nil { return nil, err } // Deserialize the transaction and add it to the result slice. var msgTx wire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, err } msgTxns = append(msgTxns, &msgTx) } return msgTxns, nil }
// Receive waits for the response promised by the future and returns a // transaction given its hash. func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal result as a string. var txHex string err = json.Unmarshal(res, &txHex) if err != nil { return nil, err } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(txHex) if err != nil { return nil, err } // Deserialize the transaction and return it. var msgTx wire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } return btcutil.NewTx(&msgTx), nil }
// Receive waits for the response promised by the future and returns the // signed transaction as well as whether or not all inputs are now signed. func (r FutureSignRawTransactionResult) Receive() (*wire.MsgTx, bool, error) { res, err := receiveFuture(r) if err != nil { return nil, false, err } // Unmarshal as a signrawtransaction result. var signRawTxResult btcjson.SignRawTransactionResult err = json.Unmarshal(res, &signRawTxResult) if err != nil { return nil, false, err } // Decode the serialized transaction hex to raw bytes. serializedTx, err := hex.DecodeString(signRawTxResult.Hex) if err != nil { return nil, false, err } // Deserialize the transaction and return it. var msgTx wire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, false, err } return &msgTx, signRawTxResult.Complete, nil }
// TestTxSerialize tests MsgTx serialize and deserialize. func TestTxSerialize(t *testing.T) { noTx := wire.NewMsgTx() noTx.Version = 1 noTxEncoded := []byte{ 0x01, 0x00, 0x00, 0x00, // Version 0x00, // Varint for number of input transactions 0x00, // Varint for number of output transactions 0x00, 0x00, 0x00, 0x00, // Lock time } tests := []struct { in *wire.MsgTx // Message to encode out *wire.MsgTx // Expected decoded message buf []byte // Serialized data }{ // No transactions. { noTx, noTx, noTxEncoded, }, // Multiple transactions. { multiTx, multiTx, multiTxEncoded, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Serialize the transaction. var buf bytes.Buffer err := test.in.Serialize(&buf) if err != nil { t.Errorf("Serialize #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("Serialize #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Deserialize the transaction. var tx wire.MsgTx rbuf := bytes.NewReader(test.buf) err = tx.Deserialize(rbuf) if err != nil { t.Errorf("Deserialize #%d error %v", i, err) continue } if !reflect.DeepEqual(&tx, test.out) { t.Errorf("Deserialize #%d\n got: %s want: %s", i, spew.Sdump(&tx), spew.Sdump(test.out)) continue } } }
// TestTxSerializeErrors performs negative tests against wire encode and decode // of MsgTx to confirm error paths work correctly. func TestTxSerializeErrors(t *testing.T) { tests := []struct { in *wire.MsgTx // Value to encode buf []byte // Serialized data max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Force error in version. {multiTx, multiTxEncoded, 0, io.ErrShortWrite, io.EOF}, // Force error in number of transaction inputs. {multiTx, multiTxEncoded, 4, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block hash. {multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF}, // Force error in transaction input previous block output index. {multiTx, multiTxEncoded, 37, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script length. {multiTx, multiTxEncoded, 41, io.ErrShortWrite, io.EOF}, // Force error in transaction input signature script. {multiTx, multiTxEncoded, 42, io.ErrShortWrite, io.EOF}, // Force error in transaction input sequence. {multiTx, multiTxEncoded, 49, io.ErrShortWrite, io.EOF}, // Force error in number of transaction outputs. {multiTx, multiTxEncoded, 53, io.ErrShortWrite, io.EOF}, // Force error in transaction output value. {multiTx, multiTxEncoded, 54, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script length. {multiTx, multiTxEncoded, 62, io.ErrShortWrite, io.EOF}, // Force error in transaction output pk script. {multiTx, multiTxEncoded, 63, io.ErrShortWrite, io.EOF}, // Force error in transaction output lock time. {multiTx, multiTxEncoded, 130, io.ErrShortWrite, io.EOF}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Serialize the transaction. w := newFixedWriter(test.max) err := test.in.Serialize(w) if err != test.writeErr { t.Errorf("Serialize #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // Deserialize the transaction. var tx wire.MsgTx r := newFixedReader(test.max, test.buf) err = tx.Deserialize(r) if err != test.readErr { t.Errorf("Deserialize #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } } }
// 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 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 }
func spendOutput(txHash *chainhash.Hash, 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 }
// addChange adds a new output with the given amount and address, and // randomizes the index (and returns it) of the newly added output. func addChange(msgtx *wire.MsgTx, change btcutil.Amount, changeAddr btcutil.Address) (int, error) { pkScript, err := txscript.PayToAddrScript(changeAddr) if err != nil { return 0, fmt.Errorf("cannot create txout script: %s", err) } msgtx.AddTxOut(wire.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] return int(r), nil }
// NewTxFromReader returns a new instance of a bitcoin transaction given a // Reader to deserialize the transaction. See Tx. func NewTxFromReader(r io.Reader) (*Tx, error) { // Deserialize the bytes into a MsgTx. var msgTx wire.MsgTx err := msgTx.Deserialize(r) if err != nil { return nil, err } t := Tx{ msgTx: &msgTx, txIndex: TxIndexUnknown, } return &t, nil }
// 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) }
// 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) }
func NewTransactionRecord(msg *wire.MsgTx, ra *net.TCPAddr, la *net.TCPAddr) *TransactionRecord { record := &TransactionRecord{ Record: Record{ stamp: time.Now(), ra: ra, la: la, cmd: msg.Command(), }, details: NewDetailsRecord(msg), } return record }
// 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 }
// addHTLC... // NOTE: This MUST be called with stateMtx held. func (lc *LightningChannel) addHTLC(ourCommitTx, theirCommitTx *wire.MsgTx, paymentDesc *PaymentDescriptor) error { // If the HTLC is going to us, then we're the sender, otherwise they // are. var senderKey, receiverKey *btcec.PublicKey var senderRevocation, receiverRevocation []byte if paymentDesc.PayToUs { receiverKey = lc.channelState.OurCommitKey.PubKey() receiverRevocation = paymentDesc.OurRevocation[:] senderKey = lc.channelState.TheirCommitKey senderRevocation = paymentDesc.TheirRevocation[:] } else { senderKey = lc.channelState.OurCommitKey.PubKey() senderRevocation = paymentDesc.OurRevocation[:] receiverKey = lc.channelState.TheirCommitKey receiverRevocation = paymentDesc.TheirRevocation[:] } // Generate the proper redeem scripts for the HTLC output for both the // sender and the receiver. timeout := paymentDesc.Timeout rHash := paymentDesc.RHash delay := lc.channelState.CsvDelay senderPKScript, err := senderHTLCScript(timeout, delay, senderKey, receiverKey, senderRevocation[:], rHash[:]) if err != nil { return nil } receiverPKScript, err := receiverHTLCScript(timeout, delay, senderKey, receiverKey, receiverRevocation[:], rHash[:]) if err != nil { return nil } // Now that we have the redeem scripts, create the P2SH public key // script for each. senderP2SH, err := scriptHashPkScript(senderPKScript) if err != nil { return nil } receiverP2SH, err := scriptHashPkScript(receiverPKScript) if err != nil { return nil } // Add the new HTLC outputs to the respective commitment transactions. amountPending := int64(paymentDesc.Value) if paymentDesc.PayToUs { ourCommitTx.AddTxOut(wire.NewTxOut(amountPending, receiverP2SH)) theirCommitTx.AddTxOut(wire.NewTxOut(amountPending, senderP2SH)) } else { ourCommitTx.AddTxOut(wire.NewTxOut(amountPending, senderP2SH)) theirCommitTx.AddTxOut(wire.NewTxOut(amountPending, receiverP2SH)) } return nil }
// TxToString prints out some info about a transaction. for testing / debugging func TxToString(tx *wire.MsgTx) string { str := fmt.Sprintf("\t - Tx %s\n", tx.TxSha().String()) for i, in := range tx.TxIn { str += fmt.Sprintf("Input %d: %s\n", i, in.PreviousOutPoint.String()) str += fmt.Sprintf("SigScript for input %d: %x\n", i, in.SignatureScript) } for i, out := range tx.TxOut { if out != nil { str += fmt.Sprintf("\toutput %d script: %x amt: %d\n", i, out.PkScript, out.Value) } else { str += fmt.Sprintf("output %d nil (WARNING)\n", i) } } return str }
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) } }
func NewDetailsRecord(msg *wire.MsgTx) *DetailsRecord { record := &DetailsRecord{ hash: msg.TxSha(), ins: make([]*InputRecord, len(msg.TxIn)), outs: make([]*OutputRecord, len(msg.TxOut)), } for i, txin := range msg.TxIn { record.ins[i] = NewInputRecord(txin) } for i, txout := range msg.TxOut { record.outs[i] = NewOutputRecord(txout) } return record }
// 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 }
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.InvTypeTx, &txid) invMsg := wire.NewMsgInv() err = invMsg.AddInvVect(iv1) if err != nil { return err } s.outMsgQueue <- invMsg return nil }
// parseChainTxNtfnParams parses out the transaction and optional details about // the block it's mined in from the parameters of recvtx and redeemingtx // notifications. func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, *btcjson.BlockDetails, error) { if len(params) == 0 || len(params) > 2 { return nil, nil, wrongNumParams(len(params)) } // Unmarshal first parameter as a string. var txHex string err := json.Unmarshal(params[0], &txHex) if err != nil { return nil, nil, err } // If present, unmarshal second optional parameter as the block details // JSON object. var block *btcjson.BlockDetails if len(params) > 1 { err = json.Unmarshal(params[1], &block) if err != nil { return nil, nil, err } } // Hex decode and deserialize the transaction. serializedTx, err := hex.DecodeString(txHex) if err != nil { return nil, nil, err } var msgTx wire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, nil, err } // TODO: Change recvtx and redeemingtx callback signatures to use // nicer types for details about the block (block sha as a // wire.ShaHash, block time as a time.Time, etc.). return btcutil.NewTx(&msgTx), block, nil }
// addOutputs adds the given address/amount pairs as outputs to msgtx, // returning their total amount. func addOutputs(msgtx *wire.MsgTx, pairs map[string]btcutil.Amount, chainParams *chaincfg.Params) (btcutil.Amount, error) { var minAmount btcutil.Amount for addrStr, amt := range pairs { if amt <= 0 { return minAmount, ErrNonPositiveAmount } minAmount += amt addr, err := btcutil.DecodeAddress(addrStr, chainParams) if err != nil { return minAmount, fmt.Errorf("cannot decode address: %s", err) } // Add output to spend amt to addr. pkScript, err := txscript.PayToAddrScript(addr) if err != nil { return minAmount, fmt.Errorf("cannot create txout script: %s", err) } txout := wire.NewTxOut(int64(amt), pkScript) msgtx.AddTxOut(txout) } return minAmount, nil }
// IngestTx ingests a tx into wallet, dealing with both gains and losses func (t *TxStore) IngestTx(tx *wire.MsgTx) error { var match bool inTxid := tx.TxSha() for _, ktxid := range t.KnownTxids { if inTxid.IsEqual(ktxid) { match = true break // found tx match, } } if !match { return fmt.Errorf("we don't care about tx %s", inTxid.String()) } err := t.AbsorbTx(tx) if err != nil { return err } err = t.ExpellTx(tx) if err != nil { return err } // fmt.Printf("ingested tx %s total amt %d\n", inTxid.String(), t.Sum) return nil }