diff --git a/docs/psbt.md b/docs/psbt.md index e0c7c5baf..910d6d3a0 100644 --- a/docs/psbt.md +++ b/docs/psbt.md @@ -8,6 +8,10 @@ See [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) for a full description of the PSBT format and the different _roles_ that a participant in a PSBT can have. +To avoid possible malleability, all inputs to a funding transaction must be segwit +spends, meaning that P2PKH and normal P2SH cannot be used. An error will be +returned if any inputs are not segwit spends. + ## Creating/funding a PSBT The first step for every transaction that is constructed using a PSBT flow is to diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index 4d15ff53e..75b0e708d 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -249,6 +249,15 @@ func (i *PsbtIntent) Verify(packet *psbt.Packet) error { "output amount sum") } + // SumUtxoInputValues checks that packet.Inputs is non-empty. Here we + // check that each Input has a WitnessUtxo field, to avoid possible + // malleability. + for _, in := range packet.Inputs { + if in.WitnessUtxo == nil { + return fmt.Errorf("not all inputs are segwit spends") + } + } + i.PendingPsbt = packet i.State = PsbtVerified return nil diff --git a/lnwallet/chanfunding/psbt_assembler_test.go b/lnwallet/chanfunding/psbt_assembler_test.go index cc40887ad..775f87cc5 100644 --- a/lnwallet/chanfunding/psbt_assembler_test.go +++ b/lnwallet/chanfunding/psbt_assembler_test.go @@ -340,8 +340,8 @@ func TestPsbtVerify(t *testing.T) { }, }, { - name: "input correct", - expectedErr: "", + name: "missing witness-utxo field", + expectedErr: "not all inputs are segwit spends", doVerify: func(amt int64, p *psbt.Packet, i *PsbtIntent) error { @@ -370,6 +370,33 @@ func TestPsbtVerify(t *testing.T) { return i.Verify(p) }, }, + { + name: "input correct", + expectedErr: "", + doVerify: func(amt int64, p *psbt.Packet, + i *PsbtIntent) error { + + txOut := &wire.TxOut{ + Value: int64(chanCapacity/2) + 1, + } + p.UnsignedTx.TxIn = []*wire.TxIn{ + {}, + { + PreviousOutPoint: wire.OutPoint{ + Index: 0, + }, + }, + } + p.Inputs = []psbt.PInput{ + { + WitnessUtxo: txOut, + }, + { + WitnessUtxo: txOut, + }} + return i.Verify(p) + }, + }, } // Create a simple assembler and ask it to provision a channel to get @@ -401,9 +428,11 @@ func TestPsbtVerify(t *testing.T) { } err = tc.doVerify(amt, pendingPsbt, psbtIntent) - if err != nil && tc.expectedErr != "" && - err.Error() != tc.expectedErr { - + if err != nil && tc.expectedErr == "" { + t.Fatalf("unexpected error, got '%v' wanted "+ + "'%v'", err, tc.expectedErr) + } + if err != nil && err.Error() != tc.expectedErr { t.Fatalf("unexpected error, got '%v' wanted "+ "'%v'", err, tc.expectedErr) }