func BenchmarkPathPacketConstruction(b *testing.B) { route := make([]*btcec.PublicKey, NumMaxHops) for i := 0; i < NumMaxHops; i++ { privKey, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { b.Fatalf("unable to generate key: %v", privKey) } route[i] = privKey.PubKey() } var ( err error sphinxPacket *OnionPacket ) var hopPayloads [][]byte for i := 0; i < len(route); i++ { payload := bytes.Repeat([]byte{byte('A' + i)}, HopPayloadSize) hopPayloads = append(hopPayloads, payload) } d, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes.Repeat([]byte{'A'}, 32)) for i := 0; i < b.N; i++ { sphinxPacket, err = NewOnionPacket(route, d, hopPayloads, nil) if err != nil { b.Fatalf("unable to create packet: %v", err) } } s = sphinxPacket }
// This example demonstrates signing a message with a secp256k1 private key that // is first parsed form raw bytes and serializing the generated signature. func Example_signMessage() { // Decode a hex-encoded private key. pkBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2d4f87" + "20ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) // Sign a message using the private key. message := "test message" messageHash := chainhash.DoubleHashB([]byte(message)) signature, err := privKey.Sign(messageHash) if err != nil { fmt.Println(err) return } // Serialize and display the signature. fmt.Printf("Serialized Signature: %x\n", signature.Serialize()) // Verify the signature for the message using the public key. verified := signature.Verify(messageHash, pubKey) fmt.Printf("Signature Verified? %v\n", verified) // Output: // Serialized Signature: 304402201008e236fa8cd0f25df4482dddbb622e8a8b26ef0ba731719458de3ccd93805b022032f8ebe514ba5f672466eba334639282616bb3c2f0ab09998037513d1f9e3d6d // Signature Verified? true }
// PrivKey returns the private key for the address. It can fail if the address // manager is watching-only or locked, or the address does not have any keys. // // This is part of the ManagedPubKeyAddress interface implementation. func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) { // No private keys are available for a watching-only address manager. if a.manager.watchingOnly { return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil) } a.manager.mtx.Lock() defer a.manager.mtx.Unlock() // Account manager must be unlocked to decrypt the private key. if a.manager.locked { return nil, managerError(ErrLocked, errLocked, nil) } // Decrypt the key as needed. Also, make sure it's a copy since the // private key stored in memory can be cleared at any time. Otherwise // the returned private key could be invalidated from under the caller. privKeyCopy, err := a.unlock(a.manager.cryptoKeyPriv) if err != nil { return nil, err } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy) zero.Bytes(privKeyCopy) return privKey, nil }
// This example demonstrates decrypting a message using a private key that is // first parsed from raw bytes. func Example_decryptMessage() { // Decode the hex-encoded private key. pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + "5ea381e3ce20a2c086a2e388230811") if err != nil { fmt.Println(err) return } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) ciphertext, err := hex.DecodeString("35f644fbfb208bc71e57684c3c8b437402ca" + "002047a2f1b38aa1a8f1d5121778378414f708fe13ebf7b4a7bb74407288c1958969" + "00207cf4ac6057406e40f79961c973309a892732ae7a74ee96cd89823913b8b8d650" + "a44166dc61ea1c419d47077b748a9c06b8d57af72deb2819d98a9d503efc59fc8307" + "d14174f8b83354fac3ff56075162") // Try decrypting the message. plaintext, err := btcec.Decrypt(privKey, ciphertext) if err != nil { fmt.Println(err) return } fmt.Println(string(plaintext)) // Output: // test message }
// This example demonstrates encrypting a message for a public key that is first // parsed from raw bytes, then decrypting it using the corresponding private key. func Example_encryptMessage() { // Decode the hex-encoded pubkey of the recipient. pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + "21010db67ac11b1b51b651953d2") // uncompressed pubkey if err != nil { fmt.Println(err) return } pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) if err != nil { fmt.Println(err) return } // Encrypt a message decryptable by the private key corresponding to pubKey message := "test message" ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) if err != nil { fmt.Println(err) return } // Decode the hex-encoded private key. pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + "5ea381e3ce20a2c086a2e388230811") if err != nil { fmt.Println(err) return } // note that we already have corresponding pubKey privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) // Try decrypting and verify if it's the same message. plaintext, err := btcec.Decrypt(privKey, ciphertext) if err != nil { fmt.Println(err) return } fmt.Println(string(plaintext)) // Output: // test message }
// DeriveRevocationPrivKey derives the revocation private key given a node's // commitment private key, and the pre-image to a previously seen revocation // hash. Using this derived private key, a node is able to claim the output // within the commitment transaction of a node in the case that they broadcast // a previously revoked commitment transaction. // // The private key is derived as follwos: // revokePriv := commitPriv + revokePreimage mod N // // Where N is the order of the sub-group. func DeriveRevocationPrivKey(commitPrivKey *btcec.PrivateKey, revokePreimage []byte) *btcec.PrivateKey { // Convert the revocation pre-image into a scalar value so we can // manipulate it within the curve's defined finite field. revokeScalar := new(big.Int).SetBytes(revokePreimage) // To derive the revocation private key, we simply add the revocation // pre-image to the commitment private key. // // This works since: // P = G*a + G*b // = G*(a+b) // = G*p revokePriv := revokeScalar.Add(revokeScalar, commitPrivKey.D) revokePriv = revokePriv.Mod(revokePriv, btcec.S256().N) privRevoke, _ := btcec.PrivKeyFromBytes(btcec.S256(), revokePriv.Bytes()) return privRevoke }
// TestRevocationKeyDerivation tests that given a public key, and a revocation // hash, the homomorphic revocation public and private key derivation work // properly. func TestRevocationKeyDerivation(t *testing.T) { revocationPreimage := testHdSeed[:] priv, pub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) revocationPub := DeriveRevocationPubkey(pub, revocationPreimage) revocationPriv := DeriveRevocationPrivKey(priv, revocationPreimage) x, y := btcec.S256().ScalarBaseMult(revocationPriv.D.Bytes()) derivedRevPub := &btcec.PublicKey{ Curve: btcec.S256(), X: x, Y: y, } // The the revocation public key derived from the original public key, // and the one derived from the private key should be identical. if !revocationPub.IsEqual(derivedRevPub) { t.Fatalf("derived public keys don't match!") } }
func newTestRoute(numHops int) ([]*Router, *OnionPacket, error) { nodes := make([]*Router, numHops) // Create numMaxHops random sphinx nodes. for i := 0; i < len(nodes); i++ { privKey, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { return nil, nil, fmt.Errorf("Unable to generate random "+ "key for sphinx node: %v", err) } nodes[i] = NewRouter(privKey, &chaincfg.MainNetParams) } // Gather all the pub keys in the path. route := make([]*btcec.PublicKey, len(nodes)) for i := 0; i < len(nodes); i++ { route[i] = nodes[i].onionKey.PubKey() } var hopPayloads [][]byte for i := 0; i < len(nodes); i++ { payload := bytes.Repeat([]byte{byte('A' + i)}, HopPayloadSize) hopPayloads = append(hopPayloads, payload) } // Generate a forwarding message to route to the final node via the // generated intermdiates nodes above. Destination should be Hash160, // adding padding so parsing still works. sessionKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes.Repeat([]byte{'A'}, 32)) fwdMsg, err := NewOnionPacket(route, sessionKey, hopPayloads, nil) if err != nil { return nil, nil, fmt.Errorf("Unable to create forwarding "+ "message: %#v", err) } return nodes, fwdMsg, nil }
func TestWatchingWalletExport(t *testing.T) { createdAt := makeBS(0) w, err := New(dummyDir, "A wallet for testing.", []byte("banana"), tstNetParams, createdAt) if err != nil { t.Error("Error creating new wallet: " + err.Error()) return } // Maintain a set of the active addresses in the wallet. activeAddrs := make(map[addressKey]struct{}) // Add root address. activeAddrs[getAddressKey(w.LastChainedAddress())] = struct{}{} // Create watching wallet from w. ww, err := w.ExportWatchingWallet() if err != nil { t.Errorf("Could not create watching wallet: %v", err) return } // Verify correctness of wallet flags. if ww.flags.useEncryption { t.Errorf("Watching wallet marked as using encryption (but nothing to encrypt).") return } if !ww.flags.watchingOnly { t.Errorf("Wallet should be watching-only but is not marked so.") return } // Verify that all flags are set as expected. if ww.keyGenerator.flags.encrypted { t.Errorf("Watching root address should not be encrypted (nothing to encrypt)") return } if ww.keyGenerator.flags.hasPrivKey { t.Errorf("Watching root address marked as having a private key.") return } if !ww.keyGenerator.flags.hasPubKey { t.Errorf("Watching root address marked as missing a public key.") return } if ww.keyGenerator.flags.createPrivKeyNextUnlock { t.Errorf("Watching root address marked as needing a private key to be generated later.") return } for apkh, waddr := range ww.addrMap { switch addr := waddr.(type) { case *btcAddress: if addr.flags.encrypted { t.Errorf("Chained address should not be encrypted (nothing to encrypt)") return } if addr.flags.hasPrivKey { t.Errorf("Chained address marked as having a private key.") return } if !addr.flags.hasPubKey { t.Errorf("Chained address marked as missing a public key.") return } if addr.flags.createPrivKeyNextUnlock { t.Errorf("Chained address marked as needing a private key to be generated later.") return } case *scriptAddress: t.Errorf("Chained address was a script!") return default: t.Errorf("Chained address unknown type!") return } if _, ok := activeAddrs[apkh]; !ok { t.Errorf("Address from watching wallet not found in original wallet.") return } delete(activeAddrs, apkh) } if len(activeAddrs) != 0 { t.Errorf("%v address(es) were not exported to watching wallet.", len(activeAddrs)) return } // Check that the new addresses created by each wallet match. The // original wallet is unlocked so addresses are chained with privkeys. if err := w.Unlock([]byte("banana")); err != nil { t.Errorf("Unlocking original wallet failed: %v", err) } // Test that ExtendActiveAddresses for the watching wallet match // manually requested addresses of the original wallet. var newAddrs []btcutil.Address for i := 0; i < 10; i++ { addr, err := w.NextChainedAddress(createdAt) if err != nil { t.Errorf("Cannot get next chained address for original wallet: %v", err) return } newAddrs = append(newAddrs, addr) } newWWAddrs, err := ww.ExtendActiveAddresses(10) if err != nil { t.Errorf("Cannot extend active addresses for watching wallet: %v", err) return } for i := range newAddrs { if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { t.Errorf("Extended active addresses do not match manually requested addresses.") return } } // Test ExtendActiveAddresses for the original wallet after manually // requesting addresses for the watching wallet. newWWAddrs = nil for i := 0; i < 10; i++ { addr, err := ww.NextChainedAddress(createdAt) if err != nil { t.Errorf("Cannot get next chained address for watching wallet: %v", err) return } newWWAddrs = append(newWWAddrs, addr) } newAddrs, err = w.ExtendActiveAddresses(10) if err != nil { t.Errorf("Cannot extend active addresses for original wallet: %v", err) return } for i := range newAddrs { if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { t.Errorf("Extended active addresses do not match manually requested addresses.") return } } // Test (de)serialization of watching wallet. buf := new(bytes.Buffer) _, err = ww.WriteTo(buf) if err != nil { t.Errorf("Cannot write watching wallet: %v", err) return } ww2 := new(Store) _, err = ww2.ReadFrom(buf) if err != nil { t.Errorf("Cannot read watching wallet: %v", err) return } // Check that (de)serialized watching wallet matches the exported wallet. if !reflect.DeepEqual(ww, ww2) { t.Error("Exported and read-in watching wallets do not match.") return } // Verify that nonsensical functions fail with correct error. if err := ww.Lock(); err != ErrWatchingOnly { t.Errorf("Nonsensical func Lock returned no or incorrect error: %v", err) return } if err := ww.Unlock([]byte("banana")); err != ErrWatchingOnly { t.Errorf("Nonsensical func Unlock returned no or incorrect error: %v", err) return } generator, err := ww.Address(w.keyGenerator.Address()) if err != nil { t.Errorf("generator isnt' present in wallet") } gpk := generator.(PubKeyAddress) if _, err := gpk.PrivKey(); err != ErrWatchingOnly { t.Errorf("Nonsensical func AddressKey returned no or incorrect error: %v", err) return } if _, err := ww.ExportWatchingWallet(); err != ErrWatchingOnly { t.Errorf("Nonsensical func ExportWatchingWallet returned no or incorrect error: %v", err) return } pk, _ := btcec.PrivKeyFromBytes(btcec.S256(), make([]byte, 32)) wif, err := btcutil.NewWIF(pk, tstNetParams, true) if err != nil { t.Fatal(err) } if _, err := ww.ImportPrivateKey(wif, createdAt); err != ErrWatchingOnly { t.Errorf("Nonsensical func ImportPrivateKey returned no or incorrect error: %v", err) return } }
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 0xa6, // 65-byte signature 0xac, // OP_CHECKSIG }, }, }, LockTime: 5, } testOutpoint = &wire.OutPoint{ Hash: key, Index: 0, } privKey, pubKey = btcec.PrivKeyFromBytes(btcec.S256(), key[:]) ) // makeTestDB creates a new instance of the ChannelDB for testing purposes. A // callback which cleans up the created temporary directories is also returned // and intended to be executed after the test completes. func makeTestDB() (*DB, func(), error) { // First, create a temporary directory to be used for the duration of // this test. tempDirName, err := ioutil.TempDir("", "channeldb") if err != nil { return nil, nil, err } // Next, create channeldb for the first time, also setting a mock // EncryptorDecryptor implementation for testing purposes.
func main() { args := os.Args assocData := bytes.Repeat([]byte{'B'}, 32) if len(args) == 1 { fmt.Printf("Usage: %s (generate|decode) <private-keys>\n", args[0]) } else if args[1] == "generate" { var privKeys []*btcec.PrivateKey var route []*btcec.PublicKey for i, hexKey := range args[2:] { binKey, err := hex.DecodeString(hexKey) if err != nil || len(binKey) != 32 { log.Fatalf("%s is not a valid hex privkey %s", hexKey, err) } privkey, pubkey := btcec.PrivKeyFromBytes(btcec.S256(), binKey) route = append(route, pubkey) privKeys = append(privKeys, privkey) fmt.Fprintf(os.Stderr, "Node %d pubkey %x\n", i, pubkey.SerializeCompressed()) } sessionKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes.Repeat([]byte{'A'}, 32)) var hopPayloads [][]byte for i := 0; i < len(route); i++ { payload := bytes.Repeat([]byte{'A'}, 20) hopPayloads = append(hopPayloads, payload) } msg, err := sphinx.NewOnionPacket(route, sessionKey, hopPayloads, assocData) if err != nil { log.Fatalf("Error creating message: %v", err) } w := bytes.NewBuffer([]byte{}) err = msg.Encode(w) if err != nil { log.Fatalf("Error serializing message: %v", err) } fmt.Printf("%x\n", w.Bytes()) } else if args[1] == "decode" { binKey, err := hex.DecodeString(args[2]) if len(binKey) != 32 || err != nil { log.Fatalf("Argument not a valid hex private key") } hexBytes, _ := ioutil.ReadAll(os.Stdin) binMsg, err := hex.DecodeString(strings.TrimSpace(string(hexBytes))) if err != nil { log.Fatalf("Error decoding message: %s", err) } privkey, _ := btcec.PrivKeyFromBytes(btcec.S256(), binKey) s := sphinx.NewRouter(privkey, &chaincfg.TestNet3Params) var packet sphinx.OnionPacket err = packet.Decode(bytes.NewBuffer(binMsg)) if err != nil { log.Fatalf("Error parsing message: %v", err) } p, err := s.ProcessOnionPacket(&packet, assocData) if err != nil { log.Fatalf("Failed to decode message: %s", err) } w := bytes.NewBuffer([]byte{}) err = p.Packet.Encode(w) if err != nil { log.Fatalf("Error serializing message: %v", err) } fmt.Printf("%x\n", w.Bytes()) } }
// TestCommitmentSpendValidation test the spendability of both outputs within // the commitment transaction. // // The following spending cases are covered by this test: // * Alice's spend from the delayed output on her commitment transaction. // * Bob's spend from Alice's delayed output when she broadcasts a revoked // commitment transaction. // * Bob's spend from his unencumbered output within Alice's commitment // transaction. func TestCommitmentSpendValidation(t *testing.T) { // We generate a fake output, and the corresponding txin. This output // doesn't need to exist, as we'll only be validating spending from the // transaction that references this. fundingOut := &wire.OutPoint{ Hash: testHdSeed, Index: 50, } fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil) // We also set up set some resources for the commitment transaction. // Each side currently has 1 BTC within the channel, with a total // channel capacity of 2BTC. aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) channelBalance := btcutil.Amount(1 * 10e8) csvTimeout := uint32(5) revocationPreimage := testHdSeed[:] revokePubKey := DeriveRevocationPubkey(bobKeyPub, revocationPreimage) aliceSelfOutputSigner := &mockSigner{aliceKeyPriv} // With all the test data set up, we create the commitment transaction. // We only focus on a single party's transactions, as the scripts are // identical with the roles reversed. // // This is Alice's commitment transaction, so she must wait a CSV delay // of 5 blocks before sweeping the output, while bob can spend // immediately with either the revocation key, or his regular key. commitmentTx, err := CreateCommitTx(fakeFundingTxIn, aliceKeyPub, bobKeyPub, revokePubKey, csvTimeout, channelBalance, channelBalance) if err != nil { t.Fatalf("unable to create commitment transaction: %v", nil) } delayOutput := commitmentTx.TxOut[0] regularOutput := commitmentTx.TxOut[1] // We're testing an uncooperative close, output sweep, so construct a // transaction which sweeps the funds to a random address. targetOutput, err := commitScriptUnencumbered(aliceKeyPub) if err != nil { t.Fatalf("unable to create target output: %v") } sweepTx := wire.NewMsgTx() sweepTx.Version = 2 sweepTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{commitmentTx.TxSha(), 0}, nil, nil)) sweepTx.AddTxOut(&wire.TxOut{ PkScript: targetOutput, Value: 0.5 * 10e8, }) // First, we'll test spending with Alice's key after the timeout. delayScript, err := commitScriptToSelf(csvTimeout, aliceKeyPub, revokePubKey) if err != nil { t.Fatalf("unable to generate alice delay script: %v") } sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout) signDesc := &SignDescriptor{ WitnessScript: delayScript, SigHashes: txscript.NewTxSigHashes(sweepTx), Output: &wire.TxOut{ Value: int64(channelBalance), }, HashType: txscript.SigHashAll, InputIndex: 0, } aliceWitnessSpend, err := CommitSpendTimeout(aliceSelfOutputSigner, signDesc, sweepTx) if err != nil { t.Fatalf("unable to generate delay commit spend witness :%v") } sweepTx.TxIn[0].Witness = aliceWitnessSpend vm, err := txscript.NewEngine(delayOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(channelBalance)) if err != nil { t.Fatalf("unable to create engine: %v", err) } if err := vm.Execute(); err != nil { t.Fatalf("spend from delay output is invalid: %v", err) } // Next, we'll test bob spending with the derived revocation key to // simulate the scenario when alice broadcasts this commitmen // transaction after it's been revoked. revokePrivKey := DeriveRevocationPrivKey(bobKeyPriv, revocationPreimage) bobWitnessSpend, err := commitSpendRevoke(delayScript, channelBalance, revokePrivKey, sweepTx) if err != nil { t.Fatalf("unable to generate revocation witness: %v", err) } sweepTx.TxIn[0].Witness = bobWitnessSpend vm, err = txscript.NewEngine(delayOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(channelBalance)) if err != nil { t.Fatalf("unable to create engine: %v", err) } if err := vm.Execute(); err != nil { t.Fatalf("revocation spend is invalid: %v", err) } // Finally, we test bob sweeping his output as normal in the case that // alice broadcasts this commitment transaction. bobScriptp2wkh, err := commitScriptUnencumbered(bobKeyPub) if err != nil { t.Fatalf("unable to create bob p2wkh script: %v", err) } bobRegularSpend, err := commitSpendNoDelay(bobScriptp2wkh, channelBalance, bobKeyPriv, sweepTx) if err != nil { t.Fatalf("unable to create bob regular spend: %v", err) } sweepTx.TxIn[0].Witness = bobRegularSpend vm, err = txscript.NewEngine(regularOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(channelBalance)) if err != nil { t.Fatalf("unable to create engine: %v", err) } if err := vm.Execute(); err != nil { t.Fatalf("bob p2wkh spend is invalid: %v", err) } }
// TestHTLCReceiverSpendValidation tests all possible valid+invalid redemption // paths in the script used within the reciever's commitment transaction for an // incoming HTLC. // // The following cases are exercised by this test: // * reciever spends // * HTLC redemption w/ invalid preimage size // * HTLC redemption w/ invalid sequence // * HTLC redemption w/ valid preimage size // * sender spends // * revoke w/ sig // * refund w/ invalid lock time // * refund w/ valid lock time func TestHTLCReceiverSpendValidation(t *testing.T) { // We generate a fake output, and the coresponding txin. This output // doesn't need to exist, as we'll only be validating spending from the // transaction that references this. fundingOut := &wire.OutPoint{ Hash: testHdSeed, Index: 50, } fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil) // Generate a payment and revocation pre-image to be used below. revokePreimage := testHdSeed[:] revokeHash := fastsha256.Sum256(revokePreimage) paymentPreimage := revokeHash paymentPreimage[0] ^= 1 paymentHash := fastsha256.Sum256(paymentPreimage[:]) // We'll also need some tests keys for alice and bob, and meta-data of // the HTLC output. aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) paymentAmt := btcutil.Amount(1 * 10e8) cltvTimeout := uint32(8) csvTimeout := uint32(5) // Generate the raw HTLC redemption scripts, and its p2wsh counterpart. htlcScript, err := receiverHTLCScript(cltvTimeout, csvTimeout, aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:]) if err != nil { t.Fatalf("unable to create htlc sender script: %v", err) } htlcWitnessScript, err := witnessScriptHash(htlcScript) if err != nil { t.Fatalf("unable to create p2wsh htlc script: %v", err) } // This will be Bob's commitment transaction. In this scenario Alice // is sending an HTLC to a node she has a a path to (could be Bob, // could be multiple hops down, it doesn't really matter). recieverCommitTx := wire.NewMsgTx() recieverCommitTx.AddTxIn(fakeFundingTxIn) recieverCommitTx.AddTxOut(&wire.TxOut{ Value: int64(paymentAmt), PkScript: htlcWitnessScript, }) prevOut := &wire.OutPoint{ Hash: recieverCommitTx.TxSha(), Index: 0, } sweepTx := wire.NewMsgTx() sweepTx.AddTxIn(wire.NewTxIn(prevOut, nil, nil)) sweepTx.AddTxOut( &wire.TxOut{ PkScript: []byte("doesn't matter"), Value: 1 * 10e8, }, ) testCases := []struct { witness func() wire.TxWitness valid bool }{ { // HTLC redemption w/ invalid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, bytes.Repeat([]byte{1}, 45), csvTimeout, ) }), false, }, { // HTLC redemption w/ invalid sequence makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, paymentPreimage[:], csvTimeout-2, ) }), false, }, { // HTLC redemption w/ valid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, paymentPreimage[:], csvTimeout, ) }), true, }, { // revoke w/ sig makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRevoke(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, revokePreimage[:], ) }), true, }, { // refund w/ invalid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendTimeout(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, cltvTimeout-2) }), false, }, { // refund w/ valid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendTimeout(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, cltvTimeout) }), true, }, } for i, testCase := range testCases { sweepTx.TxIn[0].Witness = testCase.witness() vm, err := txscript.NewEngine(htlcWitnessScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(paymentAmt)) if err != nil { t.Fatalf("unable to create engine: %v", err) } // This buffer will trace execution of the Script, only dumping // out to stdout in the case that a test fails. var debugBuf bytes.Buffer done := false for !done { dis, err := vm.DisasmPC() if err != nil { t.Fatalf("stepping (%v)\n", err) } debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis)) done, err = vm.Step() if err != nil && testCase.valid { fmt.Println(debugBuf.String()) t.Fatalf("spend test case #%v failed, spend should be valid: %v", i, err) } else if err == nil && !testCase.valid && done { fmt.Println(debugBuf.String()) t.Fatalf("spend test case #%v succeed, spend should be invalid: %v", i, err) } debugBuf.WriteString(fmt.Sprintf("Stack: ", vm.GetStack())) debugBuf.WriteString(fmt.Sprintf("AltStack: ", vm.GetAltStack())) } } }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 BTC. originTx := wire.NewMsgTx(wire.TxVersion) prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxHash() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx(wire.TxVersion) // Add the input(s) the redeeming transaction will spend. There is no // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0) txIn = wire.NewTxIn(prevOut, nil, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, nil, nil, -1) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
// TestCalcSequenceLock tests the LockTimeToSequence function, and the // CalcSequenceLock method of a Chain instance. The tests exercise several // combinations of inputs to the CalcSequenceLock function in order to ensure // the returned SequenceLocks are correct for each test instance. func TestCalcSequenceLock(t *testing.T) { netParams := &chaincfg.SimNetParams // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("calcseqlock", netParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // Since we're not dealing with the real block chain, disable // checkpoints and set the coinbase maturity to 1. chain.DisableCheckpoints(true) chain.TstSetCoinbaseMaturity(1) // Create a test mining address to use for the blocks we'll generate // shortly below. k := bytes.Repeat([]byte{1}, 32) _, miningPub := btcec.PrivKeyFromBytes(btcec.S256(), k) miningAddr, err := btcutil.NewAddressPubKey(miningPub.SerializeCompressed(), netParams) if err != nil { t.Fatalf("unable to generate mining addr: %v", err) } // We'll keep track of the previous block for back pointers in blocks // we generated, and also the generated blocks along with the MTP from // their PoV to aide with our relative time lock calculations. var prevBlock *btcutil.Block var blocksWithMTP []struct { block *btcutil.Block mtp time.Time } // We need to activate CSV in order to test the processing logic, so // manually craft the block version that's used to signal the soft-fork // activation. csvBit := netParams.Deployments[chaincfg.DeploymentCSV].BitNumber blockVersion := int32(0x20000000 | (uint32(1) << csvBit)) // Generate enough blocks to activate CSV, collecting each of the // blocks into a slice for later use. numBlocksToActivate := (netParams.MinerConfirmationWindow * 3) for i := uint32(0); i < numBlocksToActivate; i++ { block, err := rpctest.CreateBlock(prevBlock, nil, blockVersion, time.Time{}, miningAddr, netParams) if err != nil { t.Fatalf("unable to generate block: %v", err) } mtp := chain.BestSnapshot().MedianTime _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone) if err != nil { t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err) } if isOrphan { t.Fatalf("ProcessBlock incorrectly returned block %v "+ "is an orphan\n", i) } blocksWithMTP = append(blocksWithMTP, struct { block *btcutil.Block mtp time.Time }{ block: block, mtp: mtp, }) prevBlock = block } // Create a utxo view with all the utxos within the blocks created // above. utxoView := blockchain.NewUtxoViewpoint() for blockHeight, blockWithMTP := range blocksWithMTP { for _, tx := range blockWithMTP.block.Transactions() { utxoView.AddTxOuts(tx, int32(blockHeight)) } } utxoView.SetBestHash(blocksWithMTP[len(blocksWithMTP)-1].block.Hash()) // The median time calculated from the PoV of the best block in our // test chain. For unconfirmed inputs, this value will be used since // the MTP will be calculated from the PoV of the yet-to-be-mined // block. nextMedianTime := int64(1401292712) // We'll refer to this utxo within each input in the transactions // created below. This utxo has an age of 4 blocks and was mined within // block 297 targetTx := blocksWithMTP[len(blocksWithMTP)-4].block.Transactions()[0] utxo := wire.OutPoint{ Hash: *targetTx.Hash(), Index: 0, } // Obtain the median time past from the PoV of the input created above. // The MTP for the input is the MTP from the PoV of the block *prior* // to the one that included it. medianTime := blocksWithMTP[len(blocksWithMTP)-5].mtp.Unix() // Add an additional transaction which will serve as our unconfirmed // output. var fakeScript []byte unConfTx := &wire.MsgTx{ TxOut: []*wire.TxOut{{ PkScript: fakeScript, Value: 5, }}, } unConfUtxo := wire.OutPoint{ Hash: unConfTx.TxHash(), Index: 0, } // Adding a utxo with a height of 0x7fffffff indicates that the output // is currently unmined. utxoView.AddTxOuts(btcutil.NewTx(unConfTx), 0x7fffffff) tests := []struct { tx *btcutil.Tx view *blockchain.UtxoViewpoint want *blockchain.SequenceLock mempool bool }{ // A transaction of version one should disable sequence locks // as the new sequence number semantics only apply to // transactions version 2 or higher. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: -1, }, }, // A transaction with a single input, that a max int sequence // number. This sequence number has the high bit set, so // sequence locks should be disabled. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: wire.MaxTxInSequenceNum, }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: -1, }, }, // A transaction with a single input whose lock time is // expressed in seconds. However, the specified lock time is // below the required floor for time based lock times since // they have time granularity of 512 seconds. As a result, the // seconds lock-time should be just before the median time of // the targeted block. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime - 1, BlockHeight: -1, }, }, // A transaction with a single input whose lock time is // expressed in seconds. The number of seconds should be 1023 // seconds after the median past time of the last block in the // chain. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 1024), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + 1023, BlockHeight: -1, }, }, // A transaction with multiple inputs. The first input has a // sequence lock in blocks with a value of 4. The last input // has a sequence number with a value of 5, but has the disable // bit set. So the first lock should be selected as it's the // target lock as its the furthest in the future lock that // isn't disabled. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 5) | wire.SequenceLockTimeDisabled, }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 4), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: 299, }, }, // Transaction has a single input spending the genesis block // transaction. The input's sequence number is encodes a // relative lock-time in blocks (3 blocks). The sequence lock // should have a value of -1 for seconds, but a block height of // 298 meaning it can be included at height 299. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 298, }, }, // A transaction with two inputs with lock times expressed in // seconds. The selected sequence lock value for seconds should // be the time further in the future. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 5120), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: -1, }, }, // A transaction with two inputs with lock times expressed in // seconds. The selected sequence lock value for blocks should // be the height further in the future. The converted absolute // block height should be 302, meaning it can be included in // block 303. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 1), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 7), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 302, }, }, // A transaction with multiple inputs. Two inputs are time // based, and the other two are input maturity based. The lock // lying further into the future for both inputs should be // chosen. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 2560), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(true, 6656), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 3), }, { PreviousOutPoint: utxo, Sequence: blockchain.LockTimeToSequence(false, 9), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1, BlockHeight: 304, }, }, // A transaction with a single unconfirmed input. As the input // is confirmed, the height of the input should be interpreted // as the height of the *next* block. The current block height // is 300, so the lock time should be calculated using height // 301 as a base. A 2 block relative lock means the transaction // can be included after block 302, so in 303. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, Sequence: blockchain.LockTimeToSequence(false, 2), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: -1, BlockHeight: 302, }, }, // A transaction with a single unconfirmed input. The input has // a time based lock, so the lock time should be based off the // MTP of the *next* block. { tx: btcutil.NewTx(&wire.MsgTx{ Version: 2, TxIn: []*wire.TxIn{{ PreviousOutPoint: unConfUtxo, Sequence: blockchain.LockTimeToSequence(true, 1024), }}, }), view: utxoView, want: &blockchain.SequenceLock{ Seconds: nextMedianTime + 1023, BlockHeight: -1, }, }, } t.Logf("Running %v SequenceLock tests", len(tests)) for i, test := range tests { seqLock, err := chain.CalcSequenceLock(test.tx, test.view, test.mempool) if err != nil { t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err) } if seqLock.Seconds != test.want.Seconds { t.Fatalf("test #%d got %v seconds want %v seconds", i, seqLock.Seconds, test.want.Seconds) } if seqLock.BlockHeight != test.want.BlockHeight { t.Fatalf("test #%d got height of %v want height of %v ", i, seqLock.BlockHeight, test.want.BlockHeight) } } }
bobsPrivKey = []byte{ 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, } // Use a hard-coded HD seed. testHdSeed = [32]byte{ 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, } _, testPub = btcec.PrivKeyFromBytes(btcec.S256(), testHdSeed[:]) // The number of confirmations required to consider any created channel // open. numReqConfs = uint16(1) bobAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9000") ) // assertProperBalance asserts than the total value of the unspent outputs // within the wallet are *exactly* amount. If unable to retrieve the current // balance, or the assertion fails, the test will halt with a fatal error. func assertProperBalance(t *testing.T, lw *lnwallet.LightningWallet, numConfirms int32, amount int64) { balance, err := lw.ConfirmedBalance(numConfirms, false) if err != nil { t.Fatalf("unable to query for balance: %v", err)
"github.com/roasbeef/btcd/rpctest" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" ) var ( testPrivKey = []byte{ 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, } netParams = &chaincfg.SimNetParams privKey, pubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKey) addrPk, _ = btcutil.NewAddressPubKey(pubKey.SerializeCompressed(), netParams) testAddr = addrPk.AddressPubKeyHash() ) func getTestTxId(miner *rpctest.Harness) (*wire.ShaHash, error) { script, err := txscript.PayToAddrScript(testAddr) if err != nil { return nil, err } outputs := []*wire.TxOut{&wire.TxOut{2e8, script}} return miner.CoinbaseSpend(outputs) }
// createTestChannels creates two test channels funded witr 10 BTC, with 5 BTC // allocated to each side. func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChannel, func(), error) { aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) channelCapacity := btcutil.Amount(10 * 1e8) channelBal := channelCapacity / 2 csvTimeoutAlice := uint32(5) csvTimeoutBob := uint32(4) witnessScript, _, err := GenFundingPkScript(aliceKeyPub.SerializeCompressed(), bobKeyPub.SerializeCompressed(), int64(channelCapacity)) if err != nil { return nil, nil, nil, err } prevOut := &wire.OutPoint{ Hash: wire.ShaHash(testHdSeed), Index: 0, } fundingTxIn := wire.NewTxIn(prevOut, nil, nil) bobElkrem := elkrem.NewElkremSender(deriveElkremRoot(bobKeyPriv, bobKeyPub, aliceKeyPub)) bobFirstRevoke, err := bobElkrem.AtIndex(0) if err != nil { return nil, nil, nil, err } bobRevokeKey := DeriveRevocationPubkey(aliceKeyPub, bobFirstRevoke[:]) aliceElkrem := elkrem.NewElkremSender(deriveElkremRoot(aliceKeyPriv, aliceKeyPub, bobKeyPub)) aliceFirstRevoke, err := aliceElkrem.AtIndex(0) if err != nil { return nil, nil, nil, err } aliceRevokeKey := DeriveRevocationPubkey(bobKeyPub, aliceFirstRevoke[:]) aliceCommitTx, err := CreateCommitTx(fundingTxIn, aliceKeyPub, bobKeyPub, aliceRevokeKey, csvTimeoutAlice, channelBal, channelBal) if err != nil { return nil, nil, nil, err } bobCommitTx, err := CreateCommitTx(fundingTxIn, bobKeyPub, aliceKeyPub, bobRevokeKey, csvTimeoutBob, channelBal, channelBal) if err != nil { return nil, nil, nil, err } alicePath, err := ioutil.TempDir("", "alicedb") dbAlice, err := channeldb.Open(alicePath, &chaincfg.TestNet3Params) if err != nil { return nil, nil, nil, err } bobPath, err := ioutil.TempDir("", "bobdb") dbBob, err := channeldb.Open(bobPath, &chaincfg.TestNet3Params) if err != nil { return nil, nil, nil, err } aliceChannelState := &channeldb.OpenChannel{ IdentityPub: aliceKeyPub, ChanID: prevOut, OurCommitKey: aliceKeyPub, TheirCommitKey: bobKeyPub, Capacity: channelCapacity, OurBalance: channelBal, TheirBalance: channelBal, OurCommitTx: aliceCommitTx, FundingOutpoint: prevOut, OurMultiSigKey: aliceKeyPub, TheirMultiSigKey: bobKeyPub, FundingWitnessScript: witnessScript, LocalCsvDelay: csvTimeoutAlice, RemoteCsvDelay: csvTimeoutBob, TheirCurrentRevocation: bobRevokeKey, LocalElkrem: aliceElkrem, RemoteElkrem: &elkrem.ElkremReceiver{}, Db: dbAlice, } bobChannelState := &channeldb.OpenChannel{ IdentityPub: bobKeyPub, ChanID: prevOut, OurCommitKey: bobKeyPub, TheirCommitKey: aliceKeyPub, Capacity: channelCapacity, OurBalance: channelBal, TheirBalance: channelBal, OurCommitTx: bobCommitTx, FundingOutpoint: prevOut, OurMultiSigKey: bobKeyPub, TheirMultiSigKey: aliceKeyPub, FundingWitnessScript: witnessScript, LocalCsvDelay: csvTimeoutBob, RemoteCsvDelay: csvTimeoutAlice, TheirCurrentRevocation: aliceRevokeKey, LocalElkrem: bobElkrem, RemoteElkrem: &elkrem.ElkremReceiver{}, Db: dbBob, } cleanUpFunc := func() { os.RemoveAll(bobPath) os.RemoveAll(alicePath) } aliceSigner := &mockSigner{aliceKeyPriv} bobSigner := &mockSigner{bobKeyPriv} notifier := &mockNotfier{} channelAlice, err := NewLightningChannel(aliceSigner, nil, notifier, aliceChannelState) if err != nil { return nil, nil, nil, err } channelBob, err := NewLightningChannel(bobSigner, nil, notifier, bobChannelState) if err != nil { return nil, nil, nil, err } // Now that the channel are open, simulate the start of a session by // having Alice and Bob extend their revocation windows to each other. err = initRevocationWindows(channelAlice, channelBob, revocationWindow) if err != nil { return nil, nil, nil, err } return channelAlice, channelBob, cleanUpFunc, nil }
// preimage: 9a2cbd088763db88dd8ba79e5726daa6aba4aa7e // echo -n | openssl sha256 | openssl ripemd160 | openssl sha256 | openssl ripemd160 revocationHashBytes, _ = hex.DecodeString("4132b6b48371f7b022a16eacb9b2b0ebee134d41") revocationHash [20]byte // preimage: "hello world" redemptionHashBytes, _ = hex.DecodeString("5b315ebabb0d8c0d94281caa2dfee69a1a00436e") redemptionHash [20]byte // preimage: "next hop" nextHopBytes, _ = hex.DecodeString("94a9ded5a30fc5944cb1e2cbcd980f30616a1440") nextHop [20]byte privKeyBytes, _ = hex.DecodeString("9fa1d55217f57019a3c37f49465896b15836f54cb8ef6963870a52926420a2dd") privKey, pubKey = btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) address = pubKey // Delivery PkScript // Privkey: f2c00ead9cbcfec63098dc0a5f152c0165aff40a2ab92feb4e24869a284c32a7 // PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac") // Change PkScript // Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f // PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac") // echo -n | openssl sha256 // This stuff gets reversed!!! shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
// Test the sigscript generation for valid and invalid inputs, all // hashTypes, and with and without compression. This test creates // sigscripts to spend fake coinbase inputs, as sigscripts cannot be // created for the MsgTxs in txTests, since they come from the blockchain // and we don't have the private keys. func TestSignatureScript(t *testing.T) { t.Parallel() privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyD) nexttest: for i := range sigScriptTests { tx := wire.NewMsgTx(wire.TxVersion) output := wire.NewTxOut(500, []byte{OP_RETURN}) tx.AddTxOut(output) for range sigScriptTests[i].inputs { txin := wire.NewTxIn(coinbaseOutPoint, nil, nil) tx.AddTxIn(txin) } var script []byte var err error for j := range tx.TxIn { var idx int if sigScriptTests[i].inputs[j].indexOutOfRange { t.Errorf("at test %v", sigScriptTests[i].name) idx = len(sigScriptTests[i].inputs) } else { idx = j } script, err = SignatureScript(tx, idx, sigScriptTests[i].inputs[j].txout.PkScript, sigScriptTests[i].hashType, privKey, sigScriptTests[i].compress) if (err == nil) != sigScriptTests[i].inputs[j].sigscriptGenerates { if err == nil { t.Errorf("passed test '%v' incorrectly", sigScriptTests[i].name) } else { t.Errorf("failed test '%v': %v", sigScriptTests[i].name, err) } continue nexttest } if !sigScriptTests[i].inputs[j].sigscriptGenerates { // done with this test continue nexttest } tx.TxIn[j].SignatureScript = script } // If testing using a correct sigscript but for an incorrect // index, use last input script for first input. Requires > 0 // inputs for test. if sigScriptTests[i].scriptAtWrongIndex { tx.TxIn[0].SignatureScript = script sigScriptTests[i].inputs[0].inputValidates = false } // Validate tx input scripts scriptFlags := ScriptBip16 | ScriptVerifyDERSignatures for j := range tx.TxIn { vm, err := NewEngine(sigScriptTests[i]. inputs[j].txout.PkScript, tx, j, scriptFlags, nil, nil, 0) if err != nil { t.Errorf("cannot create script vm for test %v: %v", sigScriptTests[i].name, err) continue nexttest } err = vm.Execute() if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", sigScriptTests[i].name, err) } else { t.Errorf("failed test '%v' validation: %v", sigScriptTests[i].name, err) } continue nexttest } } } }
// newBobNode generates a test "ln node" to interact with Alice (us). For the // funding transaction, bob has a single output totaling 7BTC. For our basic // test, he'll fund the channel with 5BTC, leaving 2BTC to the change output. // TODO(roasbeef): proper handling of change etc. func newBobNode(miner *rpctest.Harness, amt btcutil.Amount) (*bobNode, error) { // First, parse Bob's priv key in order to obtain a key he'll use for the // multi-sig funding transaction. privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) // Next, generate an output redeemable by bob. pkHash := btcutil.Hash160(pubKey.SerializeCompressed()) bobAddr, err := btcutil.NewAddressWitnessPubKeyHash( pkHash, miner.ActiveNet) if err != nil { return nil, err } bobAddrScript, err := txscript.PayToAddrScript(bobAddr) if err != nil { return nil, err } // Give bobNode one 7 BTC output for use in creating channels. output := &wire.TxOut{7e8, bobAddrScript} mainTxid, err := miner.CoinbaseSpend([]*wire.TxOut{output}) if err != nil { return nil, err } // Mine a block in order to include the above output in a block. During // the reservation workflow, we currently test to ensure that the funding // output we're given actually exists. if _, err := miner.Node.Generate(1); err != nil { return nil, err } // Grab the transaction in order to locate the output index to Bob. tx, err := miner.Node.GetRawTransaction(mainTxid) if err != nil { return nil, err } found, index := lnwallet.FindScriptOutputIndex(tx.MsgTx(), bobAddrScript) if !found { return nil, fmt.Errorf("output to bob never created") } prevOut := wire.NewOutPoint(mainTxid, index) bobTxIn := wire.NewTxIn(prevOut, nil, nil) // Using bobs priv key above, create a change output he can spend. bobChangeOutput := wire.NewTxOut(2*1e8, bobAddrScript) // Bob's initial revocation hash is just his private key with the first // byte changed... var revocation [32]byte copy(revocation[:], bobsPrivKey) revocation[0] = 0xff // His ID is just as creative... var id [wire.HashSize]byte id[0] = 0xff return &bobNode{ id: pubKey, privKey: privKey, channelKey: pubKey, deliveryAddress: bobAddr, revocation: revocation, fundingAmt: amt, delay: 5, availableOutputs: []*wire.TxIn{bobTxIn}, changeOutputs: []*wire.TxOut{bobChangeOutput}, }, nil }
func TestLinkNodeEncodeDecode(t *testing.T) { cdb, cleanUp, err := makeTestDB() if err != nil { t.Fatalf("uanble to make test database: %v", err) } defer cleanUp() // First we'll create some initial data to use for populating our test // LinkNode instances. _, pub1 := btcec.PrivKeyFromBytes(btcec.S256(), key[:]) _, pub2 := btcec.PrivKeyFromBytes(btcec.S256(), rev[:]) addr1, err := net.ResolveTCPAddr("tcp", "10.0.0.1:9000") if err != nil { t.Fatalf("unable to create test addr: %v", err) } addr2, err := net.ResolveTCPAddr("tcp", "10.0.0.2:9000") if err != nil { t.Fatalf("unable to create test addr: %v", err) } // Create two fresh link node instances with the above dummy data, then // fully sync both instances to disk. node1 := cdb.NewLinkNode(wire.MainNet, pub1, addr1) node2 := cdb.NewLinkNode(wire.TestNet3, pub2, addr2) if err := node1.Sync(); err != nil { t.Fatalf("unable to sync node: %v", err) } if err := node2.Sync(); err != nil { t.Fatalf("unable to sync node: %v", err) } // Fetch all current link nodes from the database, they should exactly // match the two created above. originalNodes := []*LinkNode{node2, node1} linkNodes, err := cdb.FetchAllLinkNodes() if err != nil { t.Fatalf("unable to fetch nodes: %v", err) } for i, node := range linkNodes { if originalNodes[i].Network != node.Network { t.Fatalf("node networks don't match: expected %v, got %v", originalNodes[i].Network, node.Network) } originalPubkey := originalNodes[i].IdentityPub.SerializeCompressed() dbPubkey := node.IdentityPub.SerializeCompressed() if !bytes.Equal(originalPubkey, dbPubkey) { t.Fatalf("node pubkeys don't match: expected %x, got %x", originalPubkey, dbPubkey) } if originalNodes[i].LastSeen.Unix() != node.LastSeen.Unix() { t.Fatalf("last seen timestamps don't match: expected %v got %v", originalNodes[i].LastSeen.Unix(), node.LastSeen.Unix()) } if !reflect.DeepEqual(originalNodes[i].Addresses, node.Addresses) { t.Fatalf("addresses don't match: expected %v, got %v", originalNodes[i].Addresses, node.Addresses) } } // Next, we'll excercise the methods to append additionall IP // addresses, and also to update the last seen time. if err := node1.UpdateLastSeen(time.Now()); err != nil { t.Fatalf("unable to update last seen: %v", err) } if err := node1.AddAddress(addr2); err != nil { t.Fatalf("unable to update addr: %v", err) } // Fetch the same node from the databse according to its public key. node1DB, err := cdb.FetchLinkNode(pub1) if err != nil { t.Fatalf("unable to find node: %v", err) } // Both the last seen timestamp and the list of reachable addresses for // the node should be updated. if node1DB.LastSeen.Unix() != node1.LastSeen.Unix() { t.Fatalf("last seen timestamps don't match: expected %v got %v", node1.LastSeen.Unix(), node1DB.LastSeen.Unix()) } if len(node1DB.Addresses) != 2 { t.Fatalf("wrong length for node1 addrsses: expected %v, got %v", 2, len(node1DB.Addresses)) } if node1DB.Addresses[0].String() != addr1.String() { t.Fatalf("wrong address for node: expected %v, got %v", addr1.String(), node1DB.Addresses[0].String()) } if node1DB.Addresses[1].String() != addr2.String() { t.Fatalf("wrong address for node: expected %v, got %v", addr2.String(), node1DB.Addresses[1].String()) } }