// 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 }
// 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) } } }
// 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 }
// 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 }