diff --git a/psbt/psbt.go b/psbt/psbt.go index 2e593e0..6d829e3 100644 --- a/psbt/psbt.go +++ b/psbt/psbt.go @@ -212,7 +212,20 @@ func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { msgTx := wire.NewMsgTx(2) err = msgTx.Deserialize(bytes.NewReader(value)) if err != nil { - return nil, err + // If there are no inputs in this yet incomplete transaction, + // the wire package still incorrectly assumes it's encoded in + // the witness format. We can fix this by just trying the non- + // witness encoding too. If that also fails, it's probably an + // invalid transaction. + msgTx = wire.NewMsgTx(2) + err2 := msgTx.DeserializeNoWitness(bytes.NewReader(value)) + + // If the second attempt also failed, something else is wrong + // and it probably makes more sense to return the original + // error instead of the error from the workaround. + if err2 != nil { + return nil, err + } } if !validateUnsignedTX(msgTx) { return nil, ErrInvalidRawTxSigned diff --git a/psbt/psbt_test.go b/psbt/psbt_test.go index 96632d5..2edb268 100644 --- a/psbt/psbt_test.go +++ b/psbt/psbt_test.go @@ -1288,3 +1288,32 @@ func TestNonWitnessToWitness(t *testing.T) { t.Fatalf("Expected serialized transaction was not produced: %x", b.Bytes()) } } + +// TestEmptyInputSerialization tests the special serialization case for a wire +// transaction that has no inputs. +func TestEmptyInputSerialization(t *testing.T) { + // Create and serialize a new, empty PSBT. The wire package will assume + // it's a non-witness transaction, as there are no inputs. + psbt, err := New(nil, nil, 2, 0, nil) + if err != nil { + t.Fatalf("failed to create empty PSBT: %v", err) + } + var buf bytes.Buffer + err = psbt.Serialize(&buf) + if err != nil { + t.Fatalf("failed to serialize empty PSBT: %v", err) + } + + // Try to deserialize the empty transaction again. The wire package will + // assume it's a witness transaction because of the special case where + // there are no inputs. This assumption is wrong and the first attempt + // will fail. But a workaround should try again to deserialize the TX + // with the non-witness format. + psbt2, err := NewFromRawBytes(&buf, false) + if err != nil { + t.Fatalf("failed to deserialize empty PSBT: %v", err) + } + if len(psbt2.UnsignedTx.TxIn) > 0 || len(psbt2.UnsignedTx.TxOut) > 0 { + t.Fatalf("deserialized transaction not empty") + } +}