github.com/decred/dcrlnd@v0.7.6/lnwallet/chanvalidate/validate_test.go (about) 1 package chanvalidate 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/decred/dcrd/chaincfg/chainhash" 8 "github.com/decred/dcrd/dcrec" 9 "github.com/decred/dcrd/dcrec/secp256k1/v4" 10 "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" 11 "github.com/decred/dcrd/dcrutil/v4" 12 "github.com/decred/dcrd/txscript/v4" 13 "github.com/decred/dcrd/txscript/v4/sign" 14 "github.com/decred/dcrd/wire" 15 "github.com/decred/dcrlnd/input" 16 "github.com/decred/dcrlnd/lnwire" 17 ) 18 19 func privKeyFromBytes(b []byte) (*secp256k1.PrivateKey, *secp256k1.PublicKey) { 20 key := secp256k1.PrivKeyFromBytes(b) 21 return key, key.PubKey() 22 } 23 24 var ( 25 aliceKey = chainhash.Hash{ 26 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 27 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 28 0x4f, 0x2f, 0x6f, 0x25, 0x18, 0xa3, 0xef, 0xb9, 29 0x64, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 30 } 31 bobKey = chainhash.Hash{ 32 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 33 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 34 0x4f, 0x2f, 0x6f, 0x25, 0x98, 0xa3, 0xef, 0xb9, 35 0x69, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 36 } 37 38 alicePriv, alicePub = privKeyFromBytes(aliceKey[:]) 39 bobPriv, bobPub = privKeyFromBytes(bobKey[:]) 40 ) 41 42 // channelTestCtx holds shared context that will be used in all tests cases 43 // below. 44 type channelTestCtx struct { 45 fundingTx *wire.MsgTx 46 47 invalidCommitTx, validCommitTx *wire.MsgTx 48 49 chanPoint wire.OutPoint 50 cid lnwire.ShortChannelID 51 52 fundingScript []byte 53 } 54 55 // newChannelTestCtx creates a new channelCtx for use in the validation tests 56 // below. This creates a fake funding transaction, as well as an invalid and 57 // valid commitment transaction. 58 func newChannelTestCtx(chanSize int64) (*channelTestCtx, error) { 59 multiSigScript, err := input.GenMultiSigScript( 60 alicePub.SerializeCompressed(), bobPub.SerializeCompressed(), 61 ) 62 if err != nil { 63 return nil, err 64 } 65 pkScript, err := input.ScriptHashPkScript(multiSigScript) 66 if err != nil { 67 return nil, err 68 } 69 70 fundingOutput := wire.TxOut{ 71 Value: chanSize, 72 PkScript: pkScript, 73 } 74 75 fundingTx := &wire.MsgTx{ 76 TxIn: []*wire.TxIn{ 77 {}, 78 }, 79 TxOut: []*wire.TxOut{ 80 &fundingOutput, 81 { 82 Value: 9999, 83 PkScript: bytes.Repeat([]byte{'a'}, 32), 84 }, 85 { 86 Value: 99999, 87 PkScript: bytes.Repeat([]byte{'b'}, 32), 88 }, 89 }, 90 } 91 92 fundingTxHash := fundingTx.TxHash() 93 94 commitTx := &wire.MsgTx{ 95 TxIn: []*wire.TxIn{ 96 { 97 PreviousOutPoint: wire.OutPoint{ 98 Hash: fundingTxHash, 99 Index: 0, 100 }, 101 }, 102 }, 103 TxOut: []*wire.TxOut{ 104 &fundingOutput, 105 }, 106 } 107 108 aliceSigRaw, err := sign.RawTxInSignature( 109 commitTx, 0, multiSigScript, txscript.SigHashAll, alicePriv.Serialize(), 110 dcrec.STEcdsaSecp256k1, 111 ) 112 if err != nil { 113 return nil, err 114 } 115 116 aliceSig, err := ecdsa.ParseDERSignature( 117 aliceSigRaw[:len(aliceSigRaw)-1], 118 ) 119 if err != nil { 120 return nil, err 121 } 122 123 bobSigRaw, err := sign.RawTxInSignature( 124 commitTx, 0, multiSigScript, txscript.SigHashAll, bobPriv.Serialize(), 125 dcrec.STEcdsaSecp256k1, 126 ) 127 if err != nil { 128 return nil, err 129 } 130 131 bobSig, err := ecdsa.ParseDERSignature( 132 bobSigRaw[:len(bobSigRaw)-1], 133 ) 134 if err != nil { 135 return nil, err 136 } 137 138 witnessStack := input.SpendMultiSig( 139 multiSigScript, alicePub.SerializeCompressed(), aliceSig, 140 bobPub.SerializeCompressed(), bobSig, 141 ) 142 sigScript, err := input.WitnessStackToSigScript(witnessStack) 143 if err != nil { 144 return nil, err 145 } 146 commitTx.TxIn[0].SignatureScript = sigScript 147 invalidCommitTx := commitTx.Copy() 148 invalidCommitTx.TxIn[0].PreviousOutPoint.Index = 2 149 150 return &channelTestCtx{ 151 fundingTx: fundingTx, 152 validCommitTx: commitTx, 153 invalidCommitTx: invalidCommitTx, 154 chanPoint: wire.OutPoint{ 155 Hash: fundingTxHash, 156 Index: 0, 157 }, 158 cid: lnwire.ShortChannelID{ 159 TxPosition: 0, 160 }, 161 fundingScript: pkScript, 162 }, nil 163 } 164 165 // TestValidate ensures that the Validate method is able to detect all cases of 166 // invalid channels, and properly accept invalid channels. 167 func TestValidate(t *testing.T) { 168 t.Parallel() 169 170 chanSize := int64(1000000) 171 channelCtx, err := newChannelTestCtx(chanSize) 172 if err != nil { 173 t.Fatalf("unable to make channel context: %v", err) 174 } 175 176 testCases := []struct { 177 // expectedErr is the error we expect, this should be nil if 178 // the channel is valid. 179 expectedErr error 180 181 // locator is how the Validate method should find the target 182 // outpoint. 183 locator ChanLocator 184 185 // chanPoint is the expected final out point. 186 chanPoint wire.OutPoint 187 188 // chanScript is the funding pkScript. 189 chanScript []byte 190 191 // fundingTx is the funding transaction to use in the test. 192 fundingTx *wire.MsgTx 193 194 // commitTx is the commitment transaction to use in the test, 195 // this is optional. 196 commitTx *wire.MsgTx 197 198 // expectedValue is the value of the funding transaction we 199 // should expect. This is only required if commitTx is non-nil. 200 expectedValue int64 201 }{ 202 // Short chan ID channel locator, unable to find target 203 // outpoint. 204 { 205 expectedErr: ErrInvalidOutPoint, 206 locator: &ShortChanIDChanLocator{ 207 ID: lnwire.NewShortChanIDFromInt(9), 208 }, 209 fundingTx: &wire.MsgTx{}, 210 }, 211 212 // Chan point based channel locator, unable to find target 213 // outpoint. 214 { 215 expectedErr: ErrInvalidOutPoint, 216 locator: &OutPointChanLocator{ 217 ChanPoint: wire.OutPoint{ 218 Index: 99, 219 }, 220 }, 221 fundingTx: &wire.MsgTx{}, 222 }, 223 224 // Invalid pkScript match on mined funding transaction, chan 225 // point based locator. 226 { 227 expectedErr: ErrWrongPkScript, 228 locator: &OutPointChanLocator{ 229 ChanPoint: channelCtx.chanPoint, 230 }, 231 chanScript: bytes.Repeat([]byte("a"), 32), 232 fundingTx: channelCtx.fundingTx, 233 }, 234 235 // Invalid pkScript match on mined funding transaction, short 236 // chan ID based locator. 237 { 238 expectedErr: ErrWrongPkScript, 239 locator: &ShortChanIDChanLocator{ 240 ID: channelCtx.cid, 241 }, 242 chanScript: bytes.Repeat([]byte("a"), 32), 243 fundingTx: channelCtx.fundingTx, 244 }, 245 246 // Invalid amount on funding transaction. 247 { 248 expectedErr: ErrInvalidSize, 249 locator: &OutPointChanLocator{ 250 ChanPoint: channelCtx.chanPoint, 251 }, 252 chanScript: channelCtx.fundingScript, 253 fundingTx: channelCtx.fundingTx, 254 expectedValue: 555, 255 commitTx: channelCtx.validCommitTx, 256 }, 257 258 // Validation failure on final commitment transaction 259 { 260 expectedErr: &ErrScriptValidateError{}, 261 locator: &OutPointChanLocator{ 262 ChanPoint: channelCtx.chanPoint, 263 }, 264 chanScript: channelCtx.fundingScript, 265 fundingTx: channelCtx.fundingTx, 266 expectedValue: chanSize, 267 commitTx: channelCtx.invalidCommitTx, 268 }, 269 270 // Fully valid 3rd party verification. 271 { 272 expectedErr: nil, 273 locator: &OutPointChanLocator{ 274 ChanPoint: channelCtx.chanPoint, 275 }, 276 chanScript: channelCtx.fundingScript, 277 fundingTx: channelCtx.fundingTx, 278 chanPoint: channelCtx.chanPoint, 279 }, 280 281 // Fully valid self-channel verification. 282 { 283 expectedErr: nil, 284 locator: &OutPointChanLocator{ 285 ChanPoint: channelCtx.chanPoint, 286 }, 287 chanScript: channelCtx.fundingScript, 288 fundingTx: channelCtx.fundingTx, 289 expectedValue: chanSize, 290 commitTx: channelCtx.validCommitTx, 291 chanPoint: channelCtx.chanPoint, 292 }, 293 } 294 295 for i, testCase := range testCases { 296 ctx := &Context{ 297 Locator: testCase.locator, 298 MultiSigPkScript: testCase.chanScript, 299 FundingTx: testCase.fundingTx, 300 } 301 302 if testCase.commitTx != nil { 303 ctx.CommitCtx = &CommitmentContext{ 304 Value: dcrutil.Amount( 305 testCase.expectedValue, 306 ), 307 FullySignedCommitTx: testCase.commitTx, 308 } 309 } 310 311 chanPoint, err := Validate(ctx) 312 if err != testCase.expectedErr { 313 _, ok := testCase.expectedErr.(*ErrScriptValidateError) 314 _, scriptErr := err.(*ErrScriptValidateError) 315 if ok && scriptErr { 316 continue 317 } 318 319 t.Fatalf("test #%v: validation failed: expected %v, "+ 320 "got %v", i, testCase.expectedErr, err) 321 } 322 323 if err != nil { 324 continue 325 } 326 327 if *chanPoint != testCase.chanPoint { 328 t.Fatalf("test #%v: wrong outpoint: want %v, got %v", 329 i, testCase.chanPoint, chanPoint) 330 } 331 } 332 }