// 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 }
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: // - 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 }
// 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 spentOPs [][]byte 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. // before entering into db, serialize all inputs of the ingested tx for _, txin := range tx.TxIn { nOP, err := outPointToBytes(&txin.PreviousOutPoint) if err != nil { return hits, err } spentOPs = append(spentOPs, nOP) } // also generate PKscripts for all addresses (maybe keep storing these?) for _, adr := range ts.Adrs { // iterate through all our addresses aPKscript, err := txscript.PayToAddrScript(adr.PkhAdr) if err != nil { return hits, err } // iterate through all outputs of this tx for i, out := range tx.TxOut { if bytes.Equal(out.PkScript, aPKscript) { // new utxo for us var newu Utxo newu.AtHeight = height newu.KeyIdx = adr.KeyIdx newu.Value = out.Value var newop wire.OutPoint newop.Hash = tx.TxSha() newop.Index = uint32(i) newu.Op = newop b, err := newu.ToBytes() if err != nil { return hits, err } nUtxoBytes = append(nUtxoBytes, b) hits++ break // only one match } } } 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") } // first see if we lose utxos // 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 { duf.ForEach(func(k, v []byte) error { if bytes.Equal(k, nOP) { // matched, we lost utxo // do all this just to figure out value we lost x := make([]byte, len(k)+len(v)) copy(x, k) copy(x[len(k):], v) lostTxo, err := UtxoFromBytes(x) if err != nil { return err } hits++ // then delete the utxo from duf, save to old err = duf.Delete(k) if err != nil { return err } // after 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 = tx.TxSha() // spent by txid stxb, err := st.ToBytes() // serialize if err != nil { return err } err = old.Put(k, stxb) // write k:v outpoint:stxo bytes if err != nil { return err } // store this relevant tx sha := tx.TxSha() var buf bytes.Buffer tx.Serialize(&buf) err = txns.Put(sha.Bytes(), buf.Bytes()) if err != nil { return err } return nil // matched utxo k, won't match another } return nil // no match }) } // 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 } } return nil }) return hits, err }