github.com/decred/dcrlnd@v0.7.6/lnwallet/test_utils.go (about) 1 package lnwallet 2 3 import ( 4 "crypto/rand" 5 "encoding/binary" 6 "encoding/hex" 7 "io" 8 "io/ioutil" 9 prand "math/rand" 10 "net" 11 "os" 12 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/chaincfg/v3" 15 "github.com/decred/dcrd/dcrec/secp256k1/v4" 16 "github.com/decred/dcrd/dcrutil/v4" 17 "github.com/decred/dcrd/wire" 18 "github.com/decred/dcrlnd/channeldb" 19 "github.com/decred/dcrlnd/input" 20 "github.com/decred/dcrlnd/keychain" 21 "github.com/decred/dcrlnd/lnwallet/chainfee" 22 "github.com/decred/dcrlnd/lnwire" 23 "github.com/decred/dcrlnd/shachain" 24 ) 25 26 var ( 27 // For simplicity a single priv key controls all of our test outputs. 28 testWalletPrivKey = []byte{ 29 0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf, 30 0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9, 31 0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f, 32 0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90, 33 } 34 35 // We're alice :) 36 bobsPrivKey = []byte{ 37 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 38 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 39 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 40 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, 41 } 42 43 // Use a hard-coded HD seed. 44 testHdSeed = chainhash.Hash{ 45 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 46 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 47 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, 48 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, 49 } 50 51 alicePkScript = []byte{ 52 0x76, 0xa9, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac} 55 56 bobPkScript = []byte{ 57 0x76, 0xa9, 0x14, 0x11, 0x00, 0x00, 0x00, 0x00, 58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac} 60 61 // A serializable txn for testing funding txn. 62 testTx = &wire.MsgTx{ 63 Version: 1, 64 TxIn: []*wire.TxIn{ 65 { 66 PreviousOutPoint: wire.OutPoint{ 67 Hash: chainhash.Hash{}, 68 Index: 0xffffffff, 69 }, 70 SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, 71 Sequence: 0xffffffff, 72 }, 73 }, 74 TxOut: []*wire.TxOut{ 75 { 76 Value: 5000000000, 77 PkScript: []byte{ 78 0x41, // OP_DATA_65 79 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 80 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 81 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 82 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 83 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 84 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 85 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 86 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 87 0xa6, // 65-byte signature 88 0xac, // OP_CHECKSIG 89 }, 90 }, 91 }, 92 LockTime: 5, 93 } 94 95 // A valid, DER-encoded signature (taken from secp256k1 unit tests). 96 testSigBytes = []byte{ 97 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 98 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 99 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 100 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 101 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 102 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, 103 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 104 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 105 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 106 } 107 108 aliceDustLimit = dcrutil.Amount(6030) 109 bobDustLimit = dcrutil.Amount(12060) 110 111 testChannelCapacity float64 = 10 112 ) 113 114 // CreateTestChannels creates to fully populated channels to be used within 115 // testing fixtures. The channels will be returned as if the funding process 116 // has just completed. The channel itself is funded with 10 DCR, with 5 DCR 117 // allocated to each side. Within the channel, Alice is the initiator. The 118 // function also returns a "cleanup" function that is meant to be called once 119 // the test has been finalized. The clean up function will remote all temporary 120 // files created. If tweaklessCommits is true, then the commits within the 121 // channels will use the new format, otherwise the legacy format. 122 func CreateTestChannels(chanType channeldb.ChannelType) ( 123 *LightningChannel, *LightningChannel, func(), error) { 124 125 channelCapacity, err := dcrutil.NewAmount(testChannelCapacity) 126 if err != nil { 127 return nil, nil, nil, err 128 } 129 130 channelBal := channelCapacity / 2 131 csvTimeoutAlice := uint32(5) 132 csvTimeoutBob := uint32(4) 133 isAliceInitiator := true 134 135 prevOut := &wire.OutPoint{ 136 Hash: testHdSeed, 137 Index: prand.Uint32(), 138 } 139 fundingTxIn := wire.NewTxIn(prevOut, 0, nil) // TODO(decred): Need correct input value 140 141 // For each party, we'll create a distinct set of keys in order to 142 // emulate the typical set up with live channels. 143 var ( 144 aliceKeys []*secp256k1.PrivateKey 145 bobKeys []*secp256k1.PrivateKey 146 ) 147 for i := 0; i < 5; i++ { 148 key := make([]byte, len(testWalletPrivKey)) 149 copy(key, testWalletPrivKey) 150 key[0] ^= byte(i + 1) 151 152 aliceKey := secp256k1.PrivKeyFromBytes(key) 153 aliceKeys = append(aliceKeys, aliceKey) 154 155 key = make([]byte, len(bobsPrivKey)) 156 copy(key, bobsPrivKey) 157 key[0] ^= byte(i + 1) 158 159 bobKey := secp256k1.PrivKeyFromBytes(key) 160 bobKeys = append(bobKeys, bobKey) 161 } 162 163 aliceCfg := channeldb.ChannelConfig{ 164 ChannelConstraints: channeldb.ChannelConstraints{ 165 DustLimit: aliceDustLimit, 166 MaxPendingAmount: lnwire.NewMAtomsFromAtoms(channelCapacity), 167 ChanReserve: channelCapacity / 100, 168 MinHTLC: 0, 169 MaxAcceptedHtlcs: input.MaxHTLCNumber / 2, 170 CsvDelay: uint16(csvTimeoutAlice), 171 }, 172 MultiSigKey: keychain.KeyDescriptor{ 173 PubKey: aliceKeys[0].PubKey(), 174 }, 175 RevocationBasePoint: keychain.KeyDescriptor{ 176 PubKey: aliceKeys[1].PubKey(), 177 }, 178 PaymentBasePoint: keychain.KeyDescriptor{ 179 PubKey: aliceKeys[2].PubKey(), 180 }, 181 DelayBasePoint: keychain.KeyDescriptor{ 182 PubKey: aliceKeys[3].PubKey(), 183 }, 184 HtlcBasePoint: keychain.KeyDescriptor{ 185 PubKey: aliceKeys[4].PubKey(), 186 }, 187 } 188 bobCfg := channeldb.ChannelConfig{ 189 ChannelConstraints: channeldb.ChannelConstraints{ 190 DustLimit: bobDustLimit, 191 MaxPendingAmount: lnwire.NewMAtomsFromAtoms(channelCapacity), 192 ChanReserve: channelCapacity / 100, 193 MinHTLC: 0, 194 MaxAcceptedHtlcs: input.MaxHTLCNumber / 2, 195 CsvDelay: uint16(csvTimeoutBob), 196 }, 197 MultiSigKey: keychain.KeyDescriptor{ 198 PubKey: bobKeys[0].PubKey(), 199 }, 200 RevocationBasePoint: keychain.KeyDescriptor{ 201 PubKey: bobKeys[1].PubKey(), 202 }, 203 PaymentBasePoint: keychain.KeyDescriptor{ 204 PubKey: bobKeys[2].PubKey(), 205 }, 206 DelayBasePoint: keychain.KeyDescriptor{ 207 PubKey: bobKeys[3].PubKey(), 208 }, 209 HtlcBasePoint: keychain.KeyDescriptor{ 210 PubKey: bobKeys[4].PubKey(), 211 }, 212 } 213 214 bobRoot, err := shachain.NewHash(bobKeys[0].Serialize()) 215 if err != nil { 216 return nil, nil, nil, err 217 } 218 bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) 219 bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) 220 if err != nil { 221 return nil, nil, nil, err 222 } 223 bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:]) 224 225 aliceRoot, err := shachain.NewHash(aliceKeys[0].Serialize()) 226 if err != nil { 227 return nil, nil, nil, err 228 } 229 alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) 230 aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) 231 if err != nil { 232 return nil, nil, nil, err 233 } 234 aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) 235 236 netParams := chaincfg.RegNetParams() 237 238 aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns( 239 channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, 240 bobCommitPoint, *fundingTxIn, chanType, isAliceInitiator, 0, netParams, 241 ) 242 if err != nil { 243 return nil, nil, nil, err 244 } 245 246 alicePath, err := ioutil.TempDir("", "alicedb") 247 if err != nil { 248 return nil, nil, nil, err 249 } 250 dbAlice, err := channeldb.Open(alicePath) 251 if err != nil { 252 return nil, nil, nil, err 253 } 254 255 bobPath, err := ioutil.TempDir("", "bobdb") 256 if err != nil { 257 return nil, nil, nil, err 258 } 259 dbBob, err := channeldb.Open(bobPath) 260 if err != nil { 261 return nil, nil, nil, err 262 } 263 264 // The rate for this estimator must be the same as what is returned by 265 // calcStaticFee(). 266 // 267 // Note: This is purposefully higher than the feeKBFloor (that is, the 268 // network standard relay fee) so that tests can try both lowering and 269 // increasing the fee rate. 270 estimator := chainfee.NewStaticEstimator(1e5, 0) 271 feePerKB, err := estimator.EstimateFeePerKB(1) 272 if err != nil { 273 return nil, nil, nil, err 274 } 275 commitFee := calcStaticFee(chanType, 0) 276 var anchorAmt dcrutil.Amount 277 if chanType.HasAnchors() { 278 anchorAmt += 2 * anchorSize 279 } 280 281 aliceBalance := lnwire.NewMAtomsFromAtoms( 282 channelBal - commitFee - anchorAmt, 283 ) 284 bobBalance := lnwire.NewMAtomsFromAtoms(channelBal) 285 286 aliceLocalCommit := channeldb.ChannelCommitment{ 287 CommitHeight: 0, 288 LocalBalance: aliceBalance, 289 RemoteBalance: bobBalance, 290 CommitFee: commitFee, 291 FeePerKB: dcrutil.Amount(feePerKB), 292 CommitTx: aliceCommitTx, 293 CommitSig: testSigBytes, 294 } 295 aliceRemoteCommit := channeldb.ChannelCommitment{ 296 CommitHeight: 0, 297 LocalBalance: aliceBalance, 298 RemoteBalance: bobBalance, 299 CommitFee: commitFee, 300 FeePerKB: dcrutil.Amount(feePerKB), 301 CommitTx: bobCommitTx, 302 CommitSig: testSigBytes, 303 } 304 bobLocalCommit := channeldb.ChannelCommitment{ 305 CommitHeight: 0, 306 LocalBalance: bobBalance, 307 RemoteBalance: aliceBalance, 308 CommitFee: commitFee, 309 FeePerKB: dcrutil.Amount(feePerKB), 310 CommitTx: bobCommitTx, 311 CommitSig: testSigBytes, 312 } 313 bobRemoteCommit := channeldb.ChannelCommitment{ 314 CommitHeight: 0, 315 LocalBalance: bobBalance, 316 RemoteBalance: aliceBalance, 317 CommitFee: commitFee, 318 FeePerKB: dcrutil.Amount(feePerKB), 319 CommitTx: aliceCommitTx, 320 CommitSig: testSigBytes, 321 } 322 323 var chanIDBytes [8]byte 324 if _, err := io.ReadFull(rand.Reader, chanIDBytes[:]); err != nil { 325 return nil, nil, nil, err 326 } 327 328 shortChanID := lnwire.NewShortChanIDFromInt( 329 binary.BigEndian.Uint64(chanIDBytes[:]), 330 ) 331 332 aliceChannelState := &channeldb.OpenChannel{ 333 LocalChanCfg: aliceCfg, 334 RemoteChanCfg: bobCfg, 335 IdentityPub: aliceKeys[0].PubKey(), 336 FundingOutpoint: *prevOut, 337 ShortChannelID: shortChanID, 338 ChanType: chanType, 339 IsInitiator: isAliceInitiator, 340 Capacity: channelCapacity, 341 RemoteCurrentRevocation: bobCommitPoint, 342 RevocationProducer: alicePreimageProducer, 343 RevocationStore: shachain.NewRevocationStore(), 344 LocalCommitment: aliceLocalCommit, 345 RemoteCommitment: aliceRemoteCommit, 346 Db: dbAlice.ChannelStateDB(), 347 Packager: channeldb.NewChannelPackager(shortChanID), 348 FundingTxn: testTx, 349 } 350 bobChannelState := &channeldb.OpenChannel{ 351 LocalChanCfg: bobCfg, 352 RemoteChanCfg: aliceCfg, 353 IdentityPub: bobKeys[0].PubKey(), 354 FundingOutpoint: *prevOut, 355 ShortChannelID: shortChanID, 356 ChanType: chanType, 357 IsInitiator: !isAliceInitiator, 358 Capacity: channelCapacity, 359 RemoteCurrentRevocation: aliceCommitPoint, 360 RevocationProducer: bobPreimageProducer, 361 RevocationStore: shachain.NewRevocationStore(), 362 LocalCommitment: bobLocalCommit, 363 RemoteCommitment: bobRemoteCommit, 364 Db: dbBob.ChannelStateDB(), 365 Packager: channeldb.NewChannelPackager(shortChanID), 366 } 367 368 aliceSigner := &input.MockSigner{Privkeys: aliceKeys} 369 bobSigner := &input.MockSigner{Privkeys: bobKeys} 370 371 // TODO(roasbeef): make mock version of pre-image store 372 373 alicePool := NewSigPool(1, aliceSigner) 374 channelAlice, err := NewLightningChannel( 375 aliceSigner, aliceChannelState, alicePool, netParams, 376 ) 377 if err != nil { 378 return nil, nil, nil, err 379 } 380 alicePool.Start() 381 382 obfuscator := createStateHintObfuscator(aliceChannelState) 383 384 bobPool := NewSigPool(1, bobSigner) 385 channelBob, err := NewLightningChannel( 386 bobSigner, bobChannelState, bobPool, netParams, 387 ) 388 if err != nil { 389 return nil, nil, nil, err 390 } 391 if err = bobPool.Start(); err != nil { 392 return nil, nil, nil, err 393 } 394 err = SetStateNumHint( 395 aliceCommitTx, 0, obfuscator, 396 ) 397 if err != nil { 398 return nil, nil, nil, err 399 } 400 err = SetStateNumHint( 401 bobCommitTx, 0, obfuscator, 402 ) 403 if err != nil { 404 return nil, nil, nil, err 405 } 406 407 addr := &net.TCPAddr{ 408 IP: net.ParseIP("127.0.0.1"), 409 Port: 18556, 410 } 411 if err := channelAlice.channelState.SyncPending(addr, 101); err != nil { 412 return nil, nil, nil, err 413 } 414 415 addr = &net.TCPAddr{ 416 IP: net.ParseIP("127.0.0.1"), 417 Port: 18555, 418 } 419 420 if err := channelBob.channelState.SyncPending(addr, 101); err != nil { 421 return nil, nil, nil, err 422 } 423 424 cleanUpFunc := func() { 425 os.RemoveAll(bobPath) 426 os.RemoveAll(alicePath) 427 428 alicePool.Stop() 429 bobPool.Stop() 430 } 431 432 // Now that the channel are open, simulate the start of a session by 433 // having Alice and Bob extend their revocation windows to each other. 434 err = initRevocationWindows(channelAlice, channelBob) 435 if err != nil { 436 return nil, nil, nil, err 437 } 438 439 return channelAlice, channelBob, cleanUpFunc, nil 440 } 441 442 // initRevocationWindows simulates a new channel being opened within the p2p 443 // network by populating the initial revocation windows of the passed 444 // commitment state machines. 445 func initRevocationWindows(chanA, chanB *LightningChannel) error { 446 aliceNextRevoke, err := chanA.NextRevocationKey() 447 if err != nil { 448 return err 449 } 450 if err := chanB.InitNextRevocation(aliceNextRevoke); err != nil { 451 return err 452 } 453 454 bobNextRevoke, err := chanB.NextRevocationKey() 455 if err != nil { 456 return err 457 } 458 if err := chanA.InitNextRevocation(bobNextRevoke); err != nil { 459 return err 460 } 461 462 return nil 463 } 464 465 // privkeyFromHex parses a Decred private key from a hex encoded string. 466 func privkeyFromHex(keyHex string) (*secp256k1.PrivateKey, error) { 467 bytes, err := hex.DecodeString(keyHex) 468 if err != nil { 469 return nil, err 470 } 471 key := secp256k1.PrivKeyFromBytes(bytes) 472 return key, nil 473 474 } 475 476 func privKeyFromBytes(b []byte) (*secp256k1.PrivateKey, *secp256k1.PublicKey) { 477 key := secp256k1.PrivKeyFromBytes(b) 478 return key, key.PubKey() 479 } 480 481 // txFromHex parses a full Decred transaction from a hex encoded string. 482 func txFromHex(txHex string) (*dcrutil.Tx, error) { 483 bytes, err := hex.DecodeString(txHex) 484 if err != nil { 485 return nil, err 486 } 487 return dcrutil.NewTxFromBytes(bytes) 488 } 489 490 // calcStaticFee calculates appropriate fees for commitment transactions. This 491 // function provides a simple way to allow test balance assertions to take fee 492 // calculations into account. 493 // 494 // This uses a fixed, hard-coded value of 6000 Atoms/kB as fee. 495 // 496 // TODO(bvu): Refactor when dynamic fee estimation is added. 497 func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) dcrutil.Amount { 498 const ( 499 feePerKB = dcrutil.Amount(1e5) 500 ) 501 502 commitSize := CommitSize(chanType) + input.HTLCOutputSize*int64(numHTLCs) 503 return feePerKB * dcrutil.Amount(commitSize) / 1000 504 } 505 506 // ForceStateTransition executes the necessary interaction between the two 507 // commitment state machines to transition to a new state locking in any 508 // pending updates. This method is useful when testing interactions between two 509 // live state machines. 510 func ForceStateTransition(chanA, chanB *LightningChannel) error { 511 aliceSig, aliceHtlcSigs, _, err := chanA.SignNextCommitment() 512 if err != nil { 513 return err 514 } 515 if err = chanB.ReceiveNewCommitment(aliceSig, aliceHtlcSigs); err != nil { 516 return err 517 } 518 519 bobRevocation, _, err := chanB.RevokeCurrentCommitment() 520 if err != nil { 521 return err 522 } 523 bobSig, bobHtlcSigs, _, err := chanB.SignNextCommitment() 524 if err != nil { 525 return err 526 } 527 528 if _, _, _, _, err := chanA.ReceiveRevocation(bobRevocation); err != nil { 529 return err 530 } 531 if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil { 532 return err 533 } 534 535 aliceRevocation, _, err := chanA.RevokeCurrentCommitment() 536 if err != nil { 537 return err 538 } 539 if _, _, _, _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil { 540 return err 541 } 542 543 return nil 544 }