Пример #1
0
func makeInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
	// Pick largest outputs first.  This is only done for compatibility with
	// previous tx creation code, not because it's a good idea.
	sort.Sort(sort.Reverse(byAmount(eligible)))

	// Current inputs and their total value.  These are closed over by the
	// returned input source and reused across multiple calls.
	currentTotal := btcutil.Amount(0)
	currentInputs := make([]*wire.TxIn, 0, len(eligible))
	currentScripts := make([][]byte, 0, len(eligible))
	currentInputValues := make([]btcutil.Amount, 0, len(eligible))

	return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn,
		[]btcutil.Amount, [][]byte, error) {

		for currentTotal < target && len(eligible) != 0 {
			nextCredit := &eligible[0]
			eligible = eligible[1:]
			nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil, nil)
			currentTotal += nextCredit.Amount
			currentInputs = append(currentInputs, nextInput)
			currentScripts = append(currentScripts, nextCredit.PkScript)
			currentInputValues = append(currentInputValues, nextCredit.Amount)
		}
		return currentTotal, currentInputs, currentInputValues, currentScripts, nil
	}
}
Пример #2
0
// selectCoinsAndChange performs coin selection in order to obtain witness
// outputs which sum to at least 'numCoins' amount of satoshis. If coin
// selection is successful/possible, then the selected coins are available
// within the passed contribution's inputs. If necessary, a change address will
// also be generated.
// TODO(roasbeef): remove hardcoded fees and req'd confs for outputs.
func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amount,
	contribution *ChannelContribution) error {

	// We hold the coin select mutex while querying for outputs, and
	// performing coin selection in order to avoid inadvertent double
	// spends across funding transactions.
	l.coinSelectMtx.Lock()
	defer l.coinSelectMtx.Unlock()

	// Find all unlocked unspent witness outputs with greater than 1
	// confirmation.
	// TODO(roasbeef): make num confs a configuration paramter
	coins, err := l.ListUnspentWitness(1)
	if err != nil {
		return err
	}

	// Perform coin selection over our available, unlocked unspent outputs
	// in order to find enough coins to meet the funding amount
	// requirements.
	selectedCoins, changeAmt, err := coinSelect(feeRate, amt, coins)
	if err != nil {
		return err
	}

	// Lock the selected coins. These coins are now "reserved", this
	// prevents concurrent funding requests from referring to and this
	// double-spending the same set of coins.
	contribution.Inputs = make([]*wire.TxIn, len(selectedCoins))
	for i, coin := range selectedCoins {
		l.lockedOutPoints[*coin] = struct{}{}
		l.LockOutpoint(*coin)

		// Empty sig script, we'll actually sign if this reservation is
		// queued up to be completed (the other side accepts).
		contribution.Inputs[i] = wire.NewTxIn(coin, nil, nil)
	}

	// Record any change output(s) generated as a result of the coin
	// selection.
	if changeAmt != 0 {
		changeAddr, err := l.NewAddress(WitnessPubKey, true)
		if err != nil {
			return err
		}
		changeScript, err := txscript.PayToAddrScript(changeAddr)
		if err != nil {
			return err
		}

		contribution.ChangeOutputs = make([]*wire.TxOut, 1)
		contribution.ChangeOutputs[0] = &wire.TxOut{
			Value:    int64(changeAmt),
			PkScript: changeScript,
		}
	}

	return nil
}
Пример #3
0
// createSpendTx generates a basic spending transaction given the passed
// signature, witness and public key scripts.
func createSpendingTx(witness [][]byte, sigScript, pkScript []byte,
	outputValue int64) *wire.MsgTx {

	coinbaseTx := wire.NewMsgTx(wire.TxVersion)

	outPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0))
	txIn := wire.NewTxIn(outPoint, []byte{OP_0, OP_0}, nil)
	txOut := wire.NewTxOut(outputValue, pkScript)
	coinbaseTx.AddTxIn(txIn)
	coinbaseTx.AddTxOut(txOut)

	spendingTx := wire.NewMsgTx(wire.TxVersion)
	coinbaseTxSha := coinbaseTx.TxHash()
	outPoint = wire.NewOutPoint(&coinbaseTxSha, 0)
	txIn = wire.NewTxIn(outPoint, sigScript, witness)
	txOut = wire.NewTxOut(outputValue, nil)

	spendingTx.AddTxIn(txIn)
	spendingTx.AddTxOut(txOut)

	return spendingTx
}
Пример #4
0
// toMsgTx generates a btcwire.MsgTx with this tx's inputs and outputs.
func (tx *withdrawalTx) toMsgTx() *wire.MsgTx {
	msgtx := wire.NewMsgTx()
	for _, o := range tx.outputs {
		msgtx.AddTxOut(wire.NewTxOut(int64(o.amount), o.pkScript()))
	}

	if tx.hasChange() {
		msgtx.AddTxOut(tx.changeOutput)
	}

	for _, i := range tx.inputs {
		msgtx.AddTxIn(wire.NewTxIn(&i.OutPoint, []byte{}))
	}
	return msgtx
}
Пример #5
0
func makeInputSource(unspents []*wire.TxOut) InputSource {
	// Return outputs in order.
	currentTotal := btcutil.Amount(0)
	currentInputs := make([]*wire.TxIn, 0, len(unspents))
	f := func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn, [][]byte, error) {
		for currentTotal < target && len(unspents) != 0 {
			u := unspents[0]
			unspents = unspents[1:]
			nextInput := wire.NewTxIn(&wire.OutPoint{}, nil)
			currentTotal += btcutil.Amount(u.Value)
			currentInputs = append(currentInputs, nextInput)
		}
		return currentTotal, currentInputs, make([][]byte, len(currentInputs)), nil
	}
	return InputSource(f)
}
Пример #6
0
// makeInputSource creates an InputSource that creates inputs for every unspent
// output with non-zero output values.  The target amount is ignored since every
// output is consumed.  The InputSource does not return any previous output
// scripts as they are not needed for creating the unsinged transaction and are
// looked up again by the wallet during the call to signrawtransaction.
func makeInputSource(outputs []btcjson.ListUnspentResult) txauthor.InputSource {
	var (
		totalInputValue btcutil.Amount
		inputs          = make([]*wire.TxIn, 0, len(outputs))
		sourceErr       error
	)
	for _, output := range outputs {
		outputAmount, err := btcutil.NewAmount(output.Amount)
		if err != nil {
			sourceErr = fmt.Errorf(
				"invalid amount `%v` in listunspent result",
				output.Amount)
			break
		}
		if outputAmount == 0 {
			continue
		}
		if !saneOutputValue(outputAmount) {
			sourceErr = fmt.Errorf(
				"impossible output amount `%v` in listunspent result",
				outputAmount)
			break
		}
		totalInputValue += outputAmount

		previousOutPoint, err := parseOutPoint(&output)
		if err != nil {
			sourceErr = fmt.Errorf(
				"invalid data in listunspent result: %v",
				err)
			break
		}

		inputs = append(inputs, wire.NewTxIn(&previousOutPoint, nil))
	}

	if sourceErr == nil && totalInputValue == 0 {
		sourceErr = noInputValue{}
	}

	return func(btcutil.Amount) (btcutil.Amount, []*wire.TxIn, [][]byte, error) {
		return totalInputValue, inputs, nil, sourceErr
	}
}
Пример #7
0
// handleSingleFunderSigs is called once the remote peer who initiated the
// single funder workflow has assembled the funding transaction, and generated
// a signature for our version of the commitment transaction. This method
// progresses the workflow by generating a signature for the remote peer's
// version of the commitment transaction.
func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
	l.limboMtx.RLock()
	pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
	l.limboMtx.RUnlock()
	if !ok {
		req.err <- fmt.Errorf("attempted to update non-existant funding state")
		return
	}

	// Grab the mutex on the ChannelReservation to ensure thead-safety
	pendingReservation.Lock()
	defer pendingReservation.Unlock()

	pendingReservation.partialState.FundingOutpoint = req.fundingOutpoint
	pendingReservation.partialState.TheirCurrentRevocation = req.revokeKey
	pendingReservation.partialState.ChanID = req.fundingOutpoint
	fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil)

	// Now that we have the funding outpoint, we can generate both versions
	// of the commitment transaction, and generate a signature for the
	// remote node's commitment transactions.
	ourCommitKey := pendingReservation.ourContribution.CommitKey
	theirCommitKey := pendingReservation.theirContribution.CommitKey
	ourBalance := pendingReservation.ourContribution.FundingAmount
	theirBalance := pendingReservation.theirContribution.FundingAmount
	ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
		pendingReservation.ourContribution.RevocationKey,
		pendingReservation.ourContribution.CsvDelay, ourBalance, theirBalance)
	if err != nil {
		req.err <- err
		return
	}
	theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
		req.revokeKey, pendingReservation.theirContribution.CsvDelay,
		theirBalance, ourBalance)
	if err != nil {
		req.err <- err
		return
	}

	// Sort both transactions according to the agreed upon cannonical
	// ordering. This ensures that both parties sign the same sighash
	// without further synchronization.
	txsort.InPlaceSort(ourCommitTx)
	pendingReservation.partialState.OurCommitTx = ourCommitTx
	txsort.InPlaceSort(theirCommitTx)

	witnessScript := pendingReservation.partialState.FundingWitnessScript
	channelValue := int64(pendingReservation.partialState.Capacity)
	hashCache := txscript.NewTxSigHashes(ourCommitTx)
	theirKey := pendingReservation.theirContribution.MultiSigKey
	ourKey := pendingReservation.partialState.OurMultiSigKey

	sigHash, err := txscript.CalcWitnessSigHash(witnessScript, hashCache,
		txscript.SigHashAll, ourCommitTx, 0, channelValue)
	if err != nil {
		req.err <- err
		return
	}

	// Verify that we've received a valid signature from the remote party
	// for our version of the commitment transaction.
	sig, err := btcec.ParseSignature(req.theirCommitmentSig, btcec.S256())
	if err != nil {
		req.err <- err
		return
	} else if !sig.Verify(sigHash, theirKey) {
		req.err <- fmt.Errorf("counterparty's commitment signature is invalid")
		return
	}
	pendingReservation.partialState.OurCommitSig = req.theirCommitmentSig

	// With their signature for our version of the commitment transactions
	// verified, we can now generate a signature for their version,
	// allowing the funding transaction to be safely broadcast.
	p2wsh, err := witnessScriptHash(witnessScript)
	if err != nil {
		req.err <- err
		return
	}
	signDesc := SignDescriptor{
		WitnessScript: witnessScript,
		PubKey:        ourKey,
		Output: &wire.TxOut{
			PkScript: p2wsh,
			Value:    channelValue,
		},
		HashType:   txscript.SigHashAll,
		SigHashes:  txscript.NewTxSigHashes(theirCommitTx),
		InputIndex: 0,
	}
	sigTheirCommit, err := l.Signer.SignOutputRaw(theirCommitTx, &signDesc)
	if err != nil {
		req.err <- err
		return
	}
	pendingReservation.ourCommitmentSig = sigTheirCommit

	req.err <- nil
}
Пример #8
0
// fundTx attempts to fund a transaction sending amt bitcoin. The coins are
// selected such that the final amount spent pays enough fees as dictated by
// the passed fee rate. The passed fee rate should be expressed in
// satoshis-per-byte.
//
// NOTE: The memWallet's mutex must be held when this function is called.
func (m *memWallet) fundTx(tx *wire.MsgTx, amt btcutil.Amount, feeRate btcutil.Amount) error {
	const (
		// spendSize is the largest number of bytes of a sigScript
		// which spends a p2pkh output: OP_DATA_73 <sig> OP_DATA_33 <pubkey>
		spendSize = 1 + 73 + 1 + 33
	)

	var (
		amtSelected btcutil.Amount
		txSize      int
	)

	for outPoint, utxo := range m.utxos {
		// Skip any outputs that are still currently immature or are
		// currently locked.
		if !utxo.isMature(m.currentHeight) || utxo.isLocked {
			continue
		}

		amtSelected += utxo.value

		// Add the selected output to the transaction, updating the
		// current tx size while accounting for the size of the future
		// sigScript.
		tx.AddTxIn(wire.NewTxIn(&outPoint, nil, nil))
		txSize = tx.SerializeSize() + spendSize*len(tx.TxIn)

		// Calculate the fee required for the txn at this point
		// observing the specified fee rate. If we don't have enough
		// coins from he current amount selected to pay the fee, then
		// continue to grab more coins.
		reqFee := btcutil.Amount(txSize * int(feeRate))
		if amtSelected-reqFee < amt {
			continue
		}

		// If we have any change left over, then add an additional
		// output to the transaction reserved for change.
		changeVal := amtSelected - amt - reqFee
		if changeVal > 0 {
			addr, err := m.newAddress()
			if err != nil {
				return err
			}
			pkScript, err := txscript.PayToAddrScript(addr)
			if err != nil {
				return err
			}
			changeOutput := &wire.TxOut{
				Value:    int64(changeVal),
				PkScript: pkScript,
			}
			tx.AddTxOut(changeOutput)
		}

		return nil
	}

	// If we've reached this point, then coin selection failed due to an
	// insufficient amount of coins.
	return fmt.Errorf("not enough funds for coin selection")
}
Пример #9
0
// 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
}
Пример #10
0
func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet.LightningWallet, t *testing.T) {
	t.Log("Running dual reservation workflow test")

	// Create the bob-test wallet which will be the other side of our funding
	// channel.
	fundingAmount := btcutil.Amount(5 * 1e8)
	bobNode, err := newBobNode(miner, fundingAmount)
	if err != nil {
		t.Fatalf("unable to create bob node: %v", err)
	}

	// Bob initiates a channel funded with 5 BTC for each side, so 10
	// BTC total. He also generates 2 BTC in change.
	chanReservation, err := wallet.InitChannelReservation(fundingAmount*2,
		fundingAmount, bobNode.id, bobAddr, numReqConfs, 4)
	if err != nil {
		t.Fatalf("unable to initialize funding reservation: %v", err)
	}

	// The channel reservation should now be populated with a multi-sig key
	// from our HD chain, a change output with 3 BTC, and 2 outputs selected
	// of 4 BTC each. Additionally, the rest of the items needed to fufill a
	// funding contribution should also have been filled in.
	ourContribution := chanReservation.OurContribution()
	if len(ourContribution.Inputs) != 2 {
		t.Fatalf("outputs for funding tx not properly selected, have %v "+
			"outputs should have 2", len(ourContribution.Inputs))
	}
	if ourContribution.MultiSigKey == nil {
		t.Fatalf("alice's key for multi-sig not found")
	}
	if ourContribution.CommitKey == nil {
		t.Fatalf("alice's key for commit not found")
	}
	if ourContribution.DeliveryAddress == nil {
		t.Fatalf("alice's final delivery address not found")
	}
	if ourContribution.CsvDelay == 0 {
		t.Fatalf("csv delay not set")
	}

	// Bob sends over his output, change addr, pub keys, initial revocation,
	// final delivery address, and his accepted csv delay for the
	// commitment transactions.
	bobContribution := bobNode.Contribution(ourContribution.CommitKey)
	if err := chanReservation.ProcessContribution(bobContribution); err != nil {
		t.Fatalf("unable to add bob's funds to the funding tx: %v", err)
	}

	// At this point, the reservation should have our signatures, and a
	// partial funding transaction (missing bob's sigs).
	theirContribution := chanReservation.TheirContribution()
	ourFundingSigs, ourCommitSig := chanReservation.OurSignatures()
	if len(ourFundingSigs) != 2 {
		t.Fatalf("only %v of our sigs present, should have 2",
			len(ourFundingSigs))
	}
	if ourCommitSig == nil {
		t.Fatalf("commitment sig not found")
	}
	if ourContribution.RevocationKey == nil {
		t.Fatalf("alice's revocation key not found")
	}

	// Additionally, the funding tx should have been populated.
	fundingTx := chanReservation.FinalFundingTx()
	if fundingTx == nil {
		t.Fatalf("funding transaction never created!")
	}

	// Their funds should also be filled in.
	if len(theirContribution.Inputs) != 1 {
		t.Fatalf("bob's outputs for funding tx not properly selected, have %v "+
			"outputs should have 2", len(theirContribution.Inputs))
	}
	if theirContribution.ChangeOutputs[0].Value != 2e8 {
		t.Fatalf("bob should have one change output with value 2e8"+
			"satoshis, is instead %v",
			theirContribution.ChangeOutputs[0].Value)
	}
	if theirContribution.MultiSigKey == nil {
		t.Fatalf("bob's key for multi-sig not found")
	}
	if theirContribution.CommitKey == nil {
		t.Fatalf("bob's key for commit tx not found")
	}
	if theirContribution.DeliveryAddress == nil {
		t.Fatalf("bob's final delivery address not found")
	}
	if theirContribution.RevocationKey == nil {
		t.Fatalf("bob's revocaiton key not found")
	}

	// TODO(roasbeef): account for current hard-coded commit fee,
	// need to remove bob all together
	chanCapacity := int64(10e8 + 5000)
	// Alice responds with her output, change addr, multi-sig key and signatures.
	// Bob then responds with his signatures.
	bobsSigs, err := bobNode.signFundingTx(fundingTx)
	if err != nil {
		t.Fatalf("unable to sign inputs for bob: %v", err)
	}
	commitSig, err := bobNode.signCommitTx(
		chanReservation.LocalCommitTx(),
		chanReservation.FundingRedeemScript(),
		chanCapacity)
	if err != nil {
		t.Fatalf("bob is unable to sign alice's commit tx: %v", err)
	}
	if err := chanReservation.CompleteReservation(bobsSigs, commitSig); err != nil {
		t.Fatalf("unable to complete funding tx: %v", err)
	}

	// At this point, the channel can be considered "open" when the funding
	// txn hits a "comfortable" depth.

	// The resulting active channel state should have been persisted to the DB.
	fundingSha := fundingTx.TxSha()
	channels, err := wallet.ChannelDB.FetchOpenChannels(bobNode.id)
	if err != nil {
		t.Fatalf("unable to retrieve channel from DB: %v", err)
	}
	if !bytes.Equal(channels[0].FundingOutpoint.Hash[:], fundingSha[:]) {
		t.Fatalf("channel state not properly saved")
	}

	// Assert that tha channel opens after a single block.
	lnc := assertChannelOpen(t, miner, uint32(numReqConfs),
		chanReservation.DispatchChan())

	// Now that the channel is open, execute a cooperative closure of the
	// now open channel.
	aliceCloseSig, _, err := lnc.InitCooperativeClose()
	if err != nil {
		t.Fatalf("unable to init cooperative closure: %v", err)
	}
	aliceCloseSig = append(aliceCloseSig, byte(txscript.SigHashAll))

	chanInfo := lnc.StateSnapshot()

	// Obtain bob's signature for the closure transaction.
	witnessScript := lnc.FundingWitnessScript
	fundingOut := lnc.ChannelPoint()
	fundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
	bobCloseTx := lnwallet.CreateCooperativeCloseTx(fundingTxIn,
		chanInfo.RemoteBalance, chanInfo.LocalBalance,
		lnc.RemoteDeliveryScript, lnc.LocalDeliveryScript,
		false)
	bobSig, err := bobNode.signCommitTx(bobCloseTx, witnessScript, int64(lnc.Capacity))
	if err != nil {
		t.Fatalf("unable to generate bob's signature for closing tx: %v", err)
	}

	// Broadcast the transaction to the network. This transaction should
	// be accepted, and found in the next mined block.
	ourKey := chanReservation.OurContribution().MultiSigKey.SerializeCompressed()
	theirKey := chanReservation.TheirContribution().MultiSigKey.SerializeCompressed()
	witness := lnwallet.SpendMultiSig(witnessScript, ourKey, aliceCloseSig,
		theirKey, bobSig)
	bobCloseTx.TxIn[0].Witness = witness
	if err := wallet.PublishTransaction(bobCloseTx); err != nil {
		t.Fatalf("broadcast of close tx rejected: %v", err)
	}
}
Пример #11
0
	// Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f
	// PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc
	changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac")

	// echo -n | openssl sha256
	// This stuff gets reversed!!!
	shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
	shaHash1, _      = wire.NewShaHash(shaHash1Bytes)
	outpoint1        = wire.NewOutPoint(shaHash1, 0)
	// echo | openssl sha256
	// This stuff gets reversed!!!
	shaHash2Bytes, _ = hex.DecodeString("01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b")
	shaHash2, _      = wire.NewShaHash(shaHash2Bytes)
	outpoint2        = wire.NewOutPoint(shaHash2, 1)
	// create inputs from outpoint1 and outpoint2
	inputs = []*wire.TxIn{wire.NewTxIn(outpoint1, nil, nil), wire.NewTxIn(outpoint2, nil, nil)}

	// Commitment Signature
	tx           = wire.NewMsgTx()
	emptybytes   = new([]byte)
	sigStr, _    = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, privKey)
	commitSig, _ = btcec.ParseSignature(sigStr, btcec.S256())

	// Funding TX Sig 1
	sig1privKeyBytes, _ = hex.DecodeString("927f5827d75dd2addeb532c0fa5ac9277565f981dd6d0d037b422be5f60bdbef")
	sig1privKey, _      = btcec.PrivKeyFromBytes(btcec.S256(), sig1privKeyBytes)
	sigStr1, _          = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig1privKey)
	commitSig1, _       = btcec.ParseSignature(sigStr1, btcec.S256())
	// Funding TX Sig 2
	sig2privKeyBytes, _ = hex.DecodeString("8a4ad188f6f4000495b765cfb6ffa591133a73019c45428ddd28f53bab551847")
	sig2privKey, _      = btcec.PrivKeyFromBytes(btcec.S256(), sig2privKeyBytes)
Пример #12
0
// 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)
	}
}
Пример #13
0
// readElement is a one-stop utility function to deserialize any datastructure
// encoded using the serialization format of lnwire.
func readElement(r io.Reader, element interface{}) error {
	var err error
	switch e := element.(type) {
	case *uint8:
		var b [1]uint8
		if _, err := r.Read(b[:]); err != nil {
			return err
		}
		*e = b[0]
	case *uint16:
		var b [2]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint16(b[:])
	case *ErrorCode:
		var b [2]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = ErrorCode(binary.BigEndian.Uint16(b[:]))
	case *CreditsAmount:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = CreditsAmount(int64(binary.BigEndian.Uint64(b[:])))
	case *uint32:
		var b [4]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint32(b[:])
	case *uint64:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint64(b[:])
	case *HTLCKey:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = HTLCKey(int64(binary.BigEndian.Uint64(b[:])))
	case *btcutil.Amount:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
	case **wire.ShaHash:
		var b wire.ShaHash
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = &b
	case **btcec.PublicKey:
		var b [33]byte
		if _, err = io.ReadFull(r, b[:]); err != nil {
			return err
		}

		pubKey, err := btcec.ParsePubKey(b[:], btcec.S256())
		if err != nil {
			return err
		}
		*e = pubKey
	case *[]uint64:
		var numItems uint16
		if err := readElement(r, &numItems); err != nil {
			return err
		}
		// if numItems > 65535 {
		// 	return fmt.Errorf("Too many items in []uint64")
		// }

		// Read the number of items
		var items []uint64
		for i := uint16(0); i < numItems; i++ {
			var item uint64
			err = readElement(r, &item)
			if err != nil {
				return err
			}
			items = append(items, item)
		}
		*e = items
	case *[]*btcec.Signature:
		var numSigs uint8
		err = readElement(r, &numSigs)
		if err != nil {
			return err
		}
		if numSigs > 127 {
			return fmt.Errorf("Too many signatures!")
		}

		// Read that number of signatures
		var sigs []*btcec.Signature
		for i := uint8(0); i < numSigs; i++ {
			sig := new(btcec.Signature)
			err = readElement(r, &sig)
			if err != nil {
				return err
			}
			sigs = append(sigs, sig)
		}
		*e = sigs
		return nil
	case **btcec.Signature:
		sigBytes, err := wire.ReadVarBytes(r, 0, 73, "signature")
		if err != nil {
			return err
		}

		sig, err := btcec.ParseSignature(sigBytes, btcec.S256())
		if err != nil {
			return err
		}
		*e = sig
	case *[][32]byte:
		// How many to read
		var sliceSize uint16
		err = readElement(r, &sliceSize)
		if err != nil {
			return err
		}

		data := make([][32]byte, 0, sliceSize)
		// Append the actual
		for i := uint16(0); i < sliceSize; i++ {
			var element [32]byte
			err = readElement(r, &element)
			if err != nil {
				return err
			}
			data = append(data, element)
		}
		*e = data
	case *[32]byte:
		if _, err = io.ReadFull(r, e[:]); err != nil {
			return err
		}
	case *wire.BitcoinNet:
		var b [4]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:]))
		return nil
	case *[]byte:
		bytes, err := wire.ReadVarBytes(r, 0, MaxSliceLength, "byte slice")
		if err != nil {
			return err
		}
		*e = bytes
	case *PkScript:
		pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript")
		if err != nil {
			return err
		}
		*e = pkScript
	case *string:
		str, err := wire.ReadVarString(r, 0)
		if err != nil {
			return err
		}
		*e = str
	case *[]*wire.TxIn:
		// Read the size (1-byte number of txins)
		var numScripts uint8
		if err := readElement(r, &numScripts); err != nil {
			return err
		}
		if numScripts > 127 {
			return fmt.Errorf("Too many txins")
		}

		// Append the actual TxIns
		txins := make([]*wire.TxIn, 0, numScripts)
		for i := uint8(0); i < numScripts; i++ {
			outpoint := new(wire.OutPoint)
			txin := wire.NewTxIn(outpoint, nil, nil)
			if err := readElement(r, &txin); err != nil {
				return err
			}
			txins = append(txins, txin)
		}
		*e = txins
	case **wire.TxIn:
		// Hash
		var h [32]byte
		if _, err = io.ReadFull(r, h[:]); err != nil {
			return err
		}
		hash, err := wire.NewShaHash(h[:])
		if err != nil {
			return err
		}
		(*e).PreviousOutPoint.Hash = *hash

		// Index
		var idxBytes [4]byte
		_, err = io.ReadFull(r, idxBytes[:])
		if err != nil {
			return err
		}
		(*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:])
		return nil
	case **wire.OutPoint:
		// TODO(roasbeef): consolidate with above
		var h [32]byte
		if _, err = io.ReadFull(r, h[:]); err != nil {
			return err
		}
		hash, err := wire.NewShaHash(h[:])
		if err != nil {
			return err
		}
		// Index
		var idxBytes [4]byte
		_, err = io.ReadFull(r, idxBytes[:])
		if err != nil {
			return err
		}
		index := binary.BigEndian.Uint32(idxBytes[:])

		*e = wire.NewOutPoint(hash, index)
	default:
		return fmt.Errorf("Unknown type in readElement: %T", e)
	}

	return nil
}
Пример #14
0
// 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
			}
		}
	}
}
Пример #15
0
// 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
}
Пример #16
0
// handleContributionMsg processes the second workflow step for the lifetime of
// a channel reservation. Upon completion, the reservation will carry a
// completed funding transaction (minus the counterparty's input signatures),
// both versions of the commitment transaction, and our signature for their
// version of the commitment transaction.
func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
	l.limboMtx.Lock()
	pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
	l.limboMtx.Unlock()
	if !ok {
		req.err <- fmt.Errorf("attempted to update non-existant funding state")
		return
	}

	// Grab the mutex on the ChannelReservation to ensure thead-safety
	pendingReservation.Lock()
	defer pendingReservation.Unlock()

	// Create a blank, fresh transaction. Soon to be a complete funding
	// transaction which will allow opening a lightning channel.
	pendingReservation.fundingTx = wire.NewMsgTx()
	fundingTx := pendingReservation.fundingTx

	// Some temporary variables to cut down on the resolution verbosity.
	pendingReservation.theirContribution = req.contribution
	theirContribution := req.contribution
	ourContribution := pendingReservation.ourContribution

	// Add all multi-party inputs and outputs to the transaction.
	for _, ourInput := range ourContribution.Inputs {
		fundingTx.AddTxIn(ourInput)
	}
	for _, theirInput := range theirContribution.Inputs {
		fundingTx.AddTxIn(theirInput)
	}
	for _, ourChangeOutput := range ourContribution.ChangeOutputs {
		fundingTx.AddTxOut(ourChangeOutput)
	}
	for _, theirChangeOutput := range theirContribution.ChangeOutputs {
		fundingTx.AddTxOut(theirChangeOutput)
	}

	ourKey := pendingReservation.partialState.OurMultiSigKey
	theirKey := theirContribution.MultiSigKey

	// Finally, add the 2-of-2 multi-sig output which will set up the lightning
	// channel.
	channelCapacity := int64(pendingReservation.partialState.Capacity)
	witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(),
		theirKey.SerializeCompressed(), channelCapacity)
	if err != nil {
		req.err <- err
		return
	}
	pendingReservation.partialState.FundingWitnessScript = witnessScript

	// Sort the transaction. Since both side agree to a canonical
	// ordering, by sorting we no longer need to send the entire
	// transaction. Only signatures will be exchanged.
	fundingTx.AddTxOut(multiSigOut)
	txsort.InPlaceSort(pendingReservation.fundingTx)

	// Next, sign all inputs that are ours, collecting the signatures in
	// order of the inputs.
	pendingReservation.ourFundingInputScripts = make([]*InputScript, 0, len(ourContribution.Inputs))
	signDesc := SignDescriptor{
		HashType:  txscript.SigHashAll,
		SigHashes: txscript.NewTxSigHashes(fundingTx),
	}
	for i, txIn := range fundingTx.TxIn {
		info, err := l.FetchInputInfo(&txIn.PreviousOutPoint)
		if err == ErrNotMine {
			continue
		} else if err != nil {
			req.err <- err
			return
		}

		signDesc.Output = info
		signDesc.InputIndex = i

		inputScript, err := l.Signer.ComputeInputScript(fundingTx, &signDesc)
		if err != nil {
			req.err <- err
			return
		}

		txIn.SignatureScript = inputScript.ScriptSig
		txIn.Witness = inputScript.Witness
		pendingReservation.ourFundingInputScripts = append(
			pendingReservation.ourFundingInputScripts,
			inputScript,
		)
	}

	// Locate the index of the multi-sig outpoint in order to record it
	// since the outputs are canonically sorted. If this is a single funder
	// workflow, then we'll also need to send this to the remote node.
	fundingTxID := fundingTx.TxSha()
	_, multiSigIndex := FindScriptOutputIndex(fundingTx, multiSigOut.PkScript)
	fundingOutpoint := wire.NewOutPoint(&fundingTxID, multiSigIndex)
	pendingReservation.partialState.FundingOutpoint = fundingOutpoint

	// Initialize an empty sha-chain for them, tracking the current pending
	// revocation hash (we don't yet know the pre-image so we can't add it
	// to the chain).
	e := &elkrem.ElkremReceiver{}
	pendingReservation.partialState.RemoteElkrem = e
	pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationKey

	masterElkremRoot, err := l.deriveMasterElkremRoot()
	if err != nil {
		req.err <- err
		return
	}

	// Now that we have their commitment key, we can create the revocation
	// key for the first version of our commitment transaction. To do so,
	// we'll first create our elkrem root, then grab the first pre-iamge
	// from it.
	elkremRoot := deriveElkremRoot(masterElkremRoot, ourKey, theirKey)
	elkremSender := elkrem.NewElkremSender(elkremRoot)
	pendingReservation.partialState.LocalElkrem = elkremSender
	firstPreimage, err := elkremSender.AtIndex(0)
	if err != nil {
		req.err <- err
		return
	}
	theirCommitKey := theirContribution.CommitKey
	ourRevokeKey := DeriveRevocationPubkey(theirCommitKey, firstPreimage[:])

	// Create the txIn to our commitment transaction; required to construct
	// the commitment transactions.
	fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingTxID, multiSigIndex), nil, nil)

	// With the funding tx complete, create both commitment transactions.
	// TODO(roasbeef): much cleanup + de-duplication
	pendingReservation.fundingLockTime = theirContribution.CsvDelay
	ourBalance := ourContribution.FundingAmount
	theirBalance := theirContribution.FundingAmount
	ourCommitKey := ourContribution.CommitKey
	ourCommitTx, err := CreateCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
		ourRevokeKey, ourContribution.CsvDelay,
		ourBalance, theirBalance)
	if err != nil {
		req.err <- err
		return
	}
	theirCommitTx, err := CreateCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
		theirContribution.RevocationKey, theirContribution.CsvDelay,
		theirBalance, ourBalance)
	if err != nil {
		req.err <- err
		return
	}

	// Sort both transactions according to the agreed upon cannonical
	// ordering. This lets us skip sending the entire transaction over,
	// instead we'll just send signatures.
	txsort.InPlaceSort(ourCommitTx)
	txsort.InPlaceSort(theirCommitTx)

	deliveryScript, err := txscript.PayToAddrScript(theirContribution.DeliveryAddress)
	if err != nil {
		req.err <- err
		return
	}

	// Record newly available information witin the open channel state.
	pendingReservation.partialState.RemoteCsvDelay = theirContribution.CsvDelay
	pendingReservation.partialState.TheirDeliveryScript = deliveryScript
	pendingReservation.partialState.ChanID = fundingOutpoint
	pendingReservation.partialState.TheirCommitKey = theirCommitKey
	pendingReservation.partialState.TheirMultiSigKey = theirContribution.MultiSigKey
	pendingReservation.partialState.OurCommitTx = ourCommitTx
	pendingReservation.ourContribution.RevocationKey = ourRevokeKey

	// Generate a signature for their version of the initial commitment
	// transaction.
	signDesc = SignDescriptor{
		WitnessScript: witnessScript,
		PubKey:        ourKey,
		Output:        multiSigOut,
		HashType:      txscript.SigHashAll,
		SigHashes:     txscript.NewTxSigHashes(theirCommitTx),
		InputIndex:    0,
	}
	sigTheirCommit, err := l.Signer.SignOutputRaw(theirCommitTx, &signDesc)
	if err != nil {
		req.err <- err
		return
	}
	pendingReservation.ourCommitmentSig = sigTheirCommit

	req.err <- nil
}
Пример #17
0
// 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()))
		}
	}
}
Пример #18
0
// TestCheckSerializedHeight tests the checkSerializedHeight function with
// various serialized heights and also does negative tests to ensure errors
// and handled properly.
func TestCheckSerializedHeight(t *testing.T) {
	// Create an empty coinbase template to be used in the tests below.
	coinbaseOutpoint := wire.NewOutPoint(&chainhash.Hash{}, math.MaxUint32)
	coinbaseTx := wire.NewMsgTx(1)
	coinbaseTx.AddTxIn(wire.NewTxIn(coinbaseOutpoint, nil, nil))

	// Expected rule errors.
	missingHeightError := blockchain.RuleError{
		ErrorCode: blockchain.ErrMissingCoinbaseHeight,
	}
	badHeightError := blockchain.RuleError{
		ErrorCode: blockchain.ErrBadCoinbaseHeight,
	}

	tests := []struct {
		sigScript  []byte // Serialized data
		wantHeight int32  // Expected height
		err        error  // Expected error type
	}{
		// No serialized height length.
		{[]byte{}, 0, missingHeightError},
		// Serialized height length with no height bytes.
		{[]byte{0x02}, 0, missingHeightError},
		// Serialized height length with too few height bytes.
		{[]byte{0x02, 0x4a}, 0, missingHeightError},
		// Serialized height that needs 2 bytes to encode.
		{[]byte{0x02, 0x4a, 0x52}, 21066, nil},
		// Serialized height that needs 2 bytes to encode, but backwards
		// endianness.
		{[]byte{0x02, 0x4a, 0x52}, 19026, badHeightError},
		// Serialized height that needs 3 bytes to encode.
		{[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil},
		// Serialized height that needs 3 bytes to encode, but backwards
		// endianness.
		{[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, badHeightError},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		msgTx := coinbaseTx.Copy()
		msgTx.TxIn[0].SignatureScript = test.sigScript
		tx := btcutil.NewTx(msgTx)

		err := blockchain.TstCheckSerializedHeight(tx, test.wantHeight)
		if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
			t.Errorf("checkSerializedHeight #%d wrong error type "+
				"got: %v <%T>, want: %T", i, err, err, test.err)
			continue
		}

		if rerr, ok := err.(blockchain.RuleError); ok {
			trerr := test.err.(blockchain.RuleError)
			if rerr.ErrorCode != trerr.ErrorCode {
				t.Errorf("checkSerializedHeight #%d wrong "+
					"error code got: %v, want: %v", i,
					rerr.ErrorCode, trerr.ErrorCode)
				continue
			}
		}
	}
}
Пример #19
0
func (s *SPVCon) SendOne(u Utxo, adr btcutil.Address) error {
	// fixed fee
	fee := int64(5000)

	sendAmt := u.Value - fee
	tx := wire.NewMsgTx() // make new tx
	// add single output
	outAdrScript, err := txscript.PayToAddrScript(adr)
	if err != nil {
		return err
	}
	// make user specified txout and add to tx
	txout := wire.NewTxOut(sendAmt, outAdrScript)
	tx.AddTxOut(txout)

	var prevPKs []byte
	if u.IsWit {
		//tx.Flags = 0x01
		wa, err := btcutil.NewAddressWitnessPubKeyHash(
			s.TS.Adrs[u.KeyIdx].PkhAdr.ScriptAddress(), s.TS.Param)
		prevPKs, err = txscript.PayToAddrScript(wa)
		if err != nil {
			return err
		}
	} else { // otherwise generate directly
		prevPKs, err = txscript.PayToAddrScript(
			s.TS.Adrs[u.KeyIdx].PkhAdr)
		if err != nil {
			return err
		}
	}

	tx.AddTxIn(wire.NewTxIn(&u.Op, prevPKs, nil))

	var sig []byte
	var wit [][]byte
	hCache := txscript.NewTxSigHashes(tx)

	child, err := s.TS.rootPrivKey.Child(u.KeyIdx + hdkeychain.HardenedKeyStart)

	if err != nil {
		return err
	}
	priv, err := child.ECPrivKey()
	if err != nil {
		return err
	}

	// This is where witness based sighash types need to happen
	// sign into stash
	if u.IsWit {
		wit, err = txscript.WitnessScript(
			tx, hCache, 0, u.Value, tx.TxIn[0].SignatureScript,
			txscript.SigHashAll, priv, true)
		if err != nil {
			return err
		}
	} else {
		sig, err = txscript.SignatureScript(
			tx, 0, tx.TxIn[0].SignatureScript,
			txscript.SigHashAll, priv, true)
		if err != nil {
			return err
		}
	}

	// swap sigs into sigScripts in txins

	if sig != nil {
		tx.TxIn[0].SignatureScript = sig
	}
	if wit != nil {
		tx.TxIn[0].Witness = wit
		tx.TxIn[0].SignatureScript = nil
	}
	return s.NewOutgoingTx(tx)
}
Пример #20
0
// 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
}
Пример #21
0
// SendCoins does send coins, but it's very rudimentary
// wit makes it into p2wpkh.  Which is not yet spendable.
func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error {
	if len(adrs) != len(sendAmts) {
		return fmt.Errorf("%d addresses and %d amounts", len(adrs), len(sendAmts))
	}
	var err error
	var score, totalSend, fee int64
	dustCutoff := int64(20000) // below this amount, just give to miners
	satPerByte := int64(80)    // satoshis per byte fee; have as arg later
	rawUtxos, err := s.TS.GetAllUtxos()
	if err != nil {
		return err
	}
	var allUtxos SortableUtxoSlice
	// start with utxos sorted by value.

	for _, utxo := range rawUtxos {
		score += utxo.Value
		allUtxos = append(allUtxos, *utxo)
	}
	// smallest and unconfirmed last (because it's reversed)
	sort.Sort(sort.Reverse(allUtxos))

	//	sort.Reverse(allUtxos)
	for _, amt := range sendAmts {
		totalSend += amt
	}
	// important rule in bitcoin, output total > input total is invalid.
	if totalSend > score {
		return fmt.Errorf("trying to send %d but %d available.",
			totalSend, score)
	}

	tx := wire.NewMsgTx() // make new tx
	// add non-change (arg) outputs
	for i, adr := range adrs {
		// make address script 76a914...88ac or 0014...
		outAdrScript, err := txscript.PayToAddrScript(adr)
		if err != nil {
			return err
		}
		// make user specified txout and add to tx
		txout := wire.NewTxOut(sendAmts[i], outAdrScript)
		tx.AddTxOut(txout)
	}

	// generate a utxo slice for your inputs
	var ins utxoSlice

	// add utxos until we've had enough
	nokori := totalSend // nokori is how much is needed on input side
	for _, utxo := range allUtxos {
		// skip unconfirmed.  Or de-prioritize?
		//		if utxo.AtHeight == 0 {
		//			continue
		//		}

		// yeah, lets add this utxo!
		ins = append(ins, utxo)
		// as we add utxos, fill in sigscripts
		// generate previous pkscripts (subscritpt?) for all utxos
		// then make txins with the utxo and prevpk, and insert them into the tx
		// these are all zeroed out during signing but it's an easy way to keep track
		var prevPKs []byte
		if utxo.IsWit {
			//tx.Flags = 0x01
			wa, err := btcutil.NewAddressWitnessPubKeyHash(
				s.TS.Adrs[utxo.KeyIdx].PkhAdr.ScriptAddress(), s.TS.Param)
			prevPKs, err = txscript.PayToAddrScript(wa)
			if err != nil {
				return err
			}
		} else { // otherwise generate directly
			prevPKs, err = txscript.PayToAddrScript(
				s.TS.Adrs[utxo.KeyIdx].PkhAdr)
			if err != nil {
				return err
			}
		}
		tx.AddTxIn(wire.NewTxIn(&utxo.Op, prevPKs, nil))
		nokori -= utxo.Value
		// if nokori is positive, don't bother checking fee yet.
		if nokori < 0 {
			fee = EstFee(tx, satPerByte)
			if nokori < -fee { // done adding utxos: nokori below negative est. fee
				break
			}
		}
	}

	// see if there's enough left to also add a change output

	changeOld, err := s.TS.NewAdr() // change is witnessy
	if err != nil {
		return err
	}
	changeAdr, err := btcutil.NewAddressWitnessPubKeyHash(
		changeOld.ScriptAddress(), s.TS.Param)
	if err != nil {
		return err
	}

	changeScript, err := txscript.PayToAddrScript(changeAdr)
	if err != nil {
		return err
	}

	changeOut := wire.NewTxOut(0, changeScript)
	tx.AddTxOut(changeOut)
	fee = EstFee(tx, satPerByte)
	changeOut.Value = -(nokori + fee)
	if changeOut.Value < dustCutoff {
		// remove last output (change) : not worth it
		tx.TxOut = tx.TxOut[:len(tx.TxOut)-1]
	}

	// sort utxos on the input side.  use this instead of txsort
	// because we want to remember which keys are associated with which inputs
	sort.Sort(ins)

	// sort tx -- this only will change txouts since inputs are already sorted
	txsort.InPlaceSort(tx)

	// tx is ready for signing,
	sigStash := make([][]byte, len(ins))
	witStash := make([][][]byte, len(ins))

	// generate tx-wide hashCache for segwit stuff
	// middle index number doesn't matter for sighashAll.
	hCache := txscript.NewTxSigHashes(tx)

	for i, txin := range tx.TxIn {
		// pick key
		child, err := s.TS.rootPrivKey.Child(
			ins[i].KeyIdx + hdkeychain.HardenedKeyStart)
		if err != nil {
			return err
		}
		priv, err := child.ECPrivKey()
		if err != nil {
			return err
		}

		// This is where witness based sighash types need to happen
		// sign into stash
		if ins[i].IsWit {
			witStash[i], err = txscript.WitnessScript(
				tx, hCache, i, ins[i].Value, txin.SignatureScript,
				txscript.SigHashAll, priv, true)
			if err != nil {
				return err
			}
		} else {
			sigStash[i], err = txscript.SignatureScript(
				tx, i, txin.SignatureScript,
				txscript.SigHashAll, priv, true)
			if err != nil {
				return err
			}
		}
	}
	// swap sigs into sigScripts in txins
	for i, txin := range tx.TxIn {
		if sigStash[i] != nil {
			txin.SignatureScript = sigStash[i]
		}
		if witStash[i] != nil {
			txin.Witness = witStash[i]
			txin.SignatureScript = nil
		}

	}

	//	fmt.Printf("tx: %s", TxToString(tx))
	//	buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))

	// send it out on the wire.  hope it gets there.
	// we should deal with rejects.  Don't yet.
	err = s.NewOutgoingTx(tx)
	if err != nil {
		return err
	}
	return nil
}
Пример #22
0
func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
	wallet *lnwallet.LightningWallet, t *testing.T) {

	t.Log("Running single funder workflow responder test")

	// For this scenario, bob will initiate the channel, while we simply act as
	// the responder.
	capacity := btcutil.Amount(4 * 1e8)

	// Create the bob-test wallet which will be initiator of a single
	// funder channel shortly.
	bobNode, err := newBobNode(miner, capacity)
	if err != nil {
		t.Fatalf("unable to create bob node: %v", err)
	}

	// Bob sends over a single funding request, so we allocate our
	// contribution and the necessary resources.
	fundingAmt := btcutil.Amount(0)
	chanReservation, err := wallet.InitChannelReservation(capacity,
		fundingAmt, bobNode.id, bobAddr, numReqConfs, 4)
	if err != nil {
		t.Fatalf("unable to init channel reservation: %v", err)
	}

	// Verify all contribution fields have been set properly. Since we are
	// the recipient of a single-funder channel, we shouldn't have selected
	// any coins or generated any change outputs.
	ourContribution := chanReservation.OurContribution()
	if len(ourContribution.Inputs) != 0 {
		t.Fatalf("outputs for funding tx not properly selected, have %v "+
			"outputs should have 0", len(ourContribution.Inputs))
	}
	if len(ourContribution.ChangeOutputs) != 0 {
		t.Fatalf("coin selection failed, should have no change outputs, "+
			"instead have: %v", ourContribution.ChangeOutputs[0].Value)
	}
	if ourContribution.MultiSigKey == nil {
		t.Fatalf("alice's key for multi-sig not found")
	}
	if ourContribution.CommitKey == nil {
		t.Fatalf("alice's key for commit not found")
	}
	if ourContribution.DeliveryAddress == nil {
		t.Fatalf("alice's final delivery address not found")
	}
	if ourContribution.CsvDelay == 0 {
		t.Fatalf("csv delay not set")
	}

	// Next we process Bob's single funder contribution which doesn't
	// include any inputs or change addresses, as only Bob will construct
	// the funding transaction.
	bobContribution := bobNode.Contribution(ourContribution.CommitKey)
	if err := chanReservation.ProcessSingleContribution(bobContribution); err != nil {
		t.Fatalf("unable to process bob's contribution: %v", err)
	}
	if chanReservation.FinalFundingTx() != nil {
		t.Fatalf("funding transaction populated!")
	}
	if len(bobContribution.Inputs) != 1 {
		t.Fatalf("bob shouldn't have one inputs, instead has %v",
			len(bobContribution.Inputs))
	}
	if ourContribution.RevocationKey == nil {
		t.Fatalf("alice's revocation key not found")
	}
	if len(bobContribution.ChangeOutputs) != 1 {
		t.Fatalf("bob shouldn't have one change output, instead "+
			"has %v", len(bobContribution.ChangeOutputs))
	}
	if bobContribution.MultiSigKey == nil {
		t.Fatalf("bob's key for multi-sig not found")
	}
	if bobContribution.CommitKey == nil {
		t.Fatalf("bob's key for commit tx not found")
	}
	if bobContribution.DeliveryAddress == nil {
		t.Fatalf("bob's final delivery address not found")
	}
	if bobContribution.RevocationKey == nil {
		t.Fatalf("bob's revocaiton key not found")
	}

	fundingRedeemScript, multiOut, err := lnwallet.GenFundingPkScript(
		ourContribution.MultiSigKey.SerializeCompressed(),
		bobContribution.MultiSigKey.SerializeCompressed(),
		// TODO(roasbeef): account for hard-coded fee, remove bob node
		int64(capacity)+5000)
	if err != nil {
		t.Fatalf("unable to generate multi-sig output: %v", err)
	}

	// At this point, we send Bob our contribution, allowing him to
	// construct the funding transaction, and sign our version of the
	// commitment transaction.
	fundingTx := wire.NewMsgTx()
	fundingTx.AddTxIn(bobNode.availableOutputs[0])
	fundingTx.AddTxOut(bobNode.changeOutputs[0])
	fundingTx.AddTxOut(multiOut)
	txsort.InPlaceSort(fundingTx)
	if _, err := bobNode.signFundingTx(fundingTx); err != nil {
		t.Fatalf("unable to generate bob's funding sigs: %v", err)
	}

	// Locate the output index of the 2-of-2 in order to send back to the
	// wallet so it can finalize the transaction by signing bob's commitment
	// transaction.
	fundingTxID := fundingTx.TxSha()
	_, multiSigIndex := lnwallet.FindScriptOutputIndex(fundingTx, multiOut.PkScript)
	fundingOutpoint := wire.NewOutPoint(&fundingTxID, multiSigIndex)

	fundingTxIn := wire.NewTxIn(fundingOutpoint, nil, nil)
	aliceCommitTx, err := lnwallet.CreateCommitTx(fundingTxIn, ourContribution.CommitKey,
		bobContribution.CommitKey, ourContribution.RevocationKey,
		ourContribution.CsvDelay, 0, capacity)
	if err != nil {
		t.Fatalf("unable to create alice's commit tx: %v", err)
	}
	txsort.InPlaceSort(aliceCommitTx)
	bobCommitSig, err := bobNode.signCommitTx(aliceCommitTx,
		// TODO(roasbeef): account for hard-coded fee, remove bob node
		fundingRedeemScript, int64(capacity)+5000)
	if err != nil {
		t.Fatalf("unable to sign alice's commit tx: %v", err)
	}

	// With this stage complete, Alice can now complete the reservation.
	bobRevokeKey := bobContribution.RevocationKey
	if err := chanReservation.CompleteReservationSingle(bobRevokeKey,
		fundingOutpoint, bobCommitSig); err != nil {
		t.Fatalf("unable to complete reservation: %v", err)
	}

	// Alice should have saved the funding output.
	if chanReservation.FundingOutpoint() != fundingOutpoint {
		t.Fatalf("funding outputs don't match: %#v vs %#v",
			chanReservation.FundingOutpoint(), fundingOutpoint)
	}

	// Some period of time later, Bob presents us with an SPV proof
	// attesting to an open channel. At this point Alice recognizes the
	// channel, saves the state to disk, and creates the channel itself.
	if _, err := chanReservation.FinalizeReservation(); err != nil {
		t.Fatalf("unable to finalize reservation: %v", err)
	}

	// TODO(roasbeef): bob verify alice's sig
}
Пример #23
0
func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
	t.Parallel()

	// Create a double spend of the received blockchain transaction.
	dupRecvTx, _ := btcutil.NewTxFromBytes(TstRecvSerializedTx)
	// Switch txout amount to 1 BTC.  Transaction store doesn't
	// validate txs, so this is fine for testing a double spend
	// removal.
	TstDupRecvAmount := int64(1e8)
	newDupMsgTx := dupRecvTx.MsgTx()
	newDupMsgTx.TxOut[0].Value = TstDupRecvAmount
	TstDoubleSpendTx := btcutil.NewTx(newDupMsgTx)
	TstDoubleSpendSerializedTx := serializeTx(TstDoubleSpendTx)

	// Create a "signed" (with invalid sigs) tx that spends output 0 of
	// the double spend.
	spendingTx := wire.NewMsgTx()
	spendingTxIn := wire.NewTxIn(wire.NewOutPoint(TstDoubleSpendTx.Sha(), 0), []byte{0, 1, 2, 3, 4})
	spendingTx.AddTxIn(spendingTxIn)
	spendingTxOut1 := wire.NewTxOut(1e7, []byte{5, 6, 7, 8, 9})
	spendingTxOut2 := wire.NewTxOut(9e7, []byte{10, 11, 12, 13, 14})
	spendingTx.AddTxOut(spendingTxOut1)
	spendingTx.AddTxOut(spendingTxOut2)
	TstSpendingTx := btcutil.NewTx(spendingTx)
	TstSpendingSerializedTx := serializeTx(TstSpendingTx)
	var _ = TstSpendingTx

	tests := []struct {
		name     string
		f        func(*Store) (*Store, error)
		bal, unc btcutil.Amount
		unspents map[wire.OutPoint]struct{}
		unmined  map[wire.ShaHash]struct{}
	}{
		{
			name: "new store",
			f: func(s *Store) (*Store, error) {
				return s, nil
			},
			bal:      0,
			unc:      0,
			unspents: map[wire.OutPoint]struct{}{},
			unmined:  map[wire.ShaHash]struct{}{},
		},
		{
			name: "txout insert",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, nil)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, nil, 0, false)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstRecvTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstRecvTx.Sha(): {},
			},
		},
		{
			name: "insert duplicate unconfirmed",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, nil)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, nil, 0, false)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstRecvTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstRecvTx.Sha(): {},
			},
		},
		{
			name: "confirmed txout insert",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
				return s, err
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstRecvTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
		{
			name: "insert duplicate confirmed",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
				return s, err
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstRecvTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
		{
			name: "rollback confirmed credit",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstRecvTxBlockDetails.Height)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstRecvTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstRecvTx.Sha(): {},
			},
		},
		{
			name: "insert confirmed double spend",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
				return s, err
			},
			bal: btcutil.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstDoubleSpendTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
		{
			name: "insert unconfirmed debit",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, nil)
				return s, err
			},
			bal:      0,
			unc:      0,
			unspents: map[wire.OutPoint]struct{}{},
			unmined: map[wire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert unconfirmed debit again",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstRecvTxBlockDetails)
				return s, err
			},
			bal:      0,
			unc:      0,
			unspents: map[wire.OutPoint]struct{}{},
			unmined: map[wire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert change (index 0)",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, nil)
				if err != nil {
					return nil, err
				}

				err = s.AddCredit(rec, nil, 0, true)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 0,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "insert output back to this own wallet (index 1)",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, nil)
				if err != nil {
					return nil, err
				}
				err = s.AddCredit(rec, nil, 1, true)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 1,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "confirm signed tx",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstSignedTxBlockDetails)
				return s, err
			},
			bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 1,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
		{
			name: "rollback after spending tx",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstSignedTxBlockDetails.Height + 1)
				return s, err
			},
			bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 1,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
		{
			name: "rollback spending tx block",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstSignedTxBlockDetails.Height)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[wire.OutPoint]struct{}{
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Sha(),
					Index: 1,
				}: {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstSpendingTx.Sha(): {},
			},
		},
		{
			name: "rollback double spend tx block",
			f: func(s *Store) (*Store, error) {
				err := s.Rollback(TstRecvTxBlockDetails.Height)
				return s, err
			},
			bal: 0,
			unc: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
			unspents: map[wire.OutPoint]struct{}{
				*wire.NewOutPoint(TstSpendingTx.Sha(), 0): {},
				*wire.NewOutPoint(TstSpendingTx.Sha(), 1): {},
			},
			unmined: map[wire.ShaHash]struct{}{
				*TstDoubleSpendTx.Sha(): {},
				*TstSpendingTx.Sha():    {},
			},
		},
		{
			name: "insert original recv txout",
			f: func(s *Store) (*Store, error) {
				rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
				if err != nil {
					return nil, err
				}
				err = s.InsertTx(rec, TstRecvTxBlockDetails)
				if err != nil {
					return nil, err
				}
				err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
				return s, err
			},
			bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
			unc: 0,
			unspents: map[wire.OutPoint]struct{}{
				*wire.NewOutPoint(TstRecvTx.Sha(), 0): {},
			},
			unmined: map[wire.ShaHash]struct{}{},
		},
	}

	s, teardown, err := testStore()
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}

	for _, test := range tests {
		tmpStore, err := test.f(s)
		if err != nil {
			t.Fatalf("%s: got error: %v", test.name, err)
		}
		s = tmpStore
		bal, err := s.Balance(1, TstRecvCurrentHeight)
		if err != nil {
			t.Fatalf("%s: Confirmed Balance failed: %v", test.name, err)
		}
		if bal != test.bal {
			t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
		}
		unc, err := s.Balance(0, TstRecvCurrentHeight)
		if err != nil {
			t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, err)
		}
		unc -= bal
		if unc != test.unc {
			t.Fatalf("%s: unconfirmed balance mismatch: expected %d, got %d", test.name, test.unc, unc)
		}

		// Check that unspent outputs match expected.
		unspent, err := s.UnspentOutputs()
		if err != nil {
			t.Fatalf("%s: failed to fetch unspent outputs: %v", test.name, err)
		}
		for _, cred := range unspent {
			if _, ok := test.unspents[cred.OutPoint]; !ok {
				t.Errorf("%s: unexpected unspent output: %v", test.name, cred.OutPoint)
			}
			delete(test.unspents, cred.OutPoint)
		}
		if len(test.unspents) != 0 {
			t.Fatalf("%s: missing expected unspent output(s)", test.name)
		}

		// Check that unmined txs match expected.
		unmined, err := s.UnminedTxs()
		if err != nil {
			t.Fatalf("%s: cannot load unmined transactions: %v", test.name, err)
		}
		for _, tx := range unmined {
			txHash := tx.TxSha()
			if _, ok := test.unmined[txHash]; !ok {
				t.Fatalf("%s: unexpected unmined tx: %v", test.name, txHash)
			}
			delete(test.unmined, txHash)
		}
		if len(test.unmined) != 0 {
			t.Fatalf("%s: missing expected unmined tx(s)", test.name)
		}

	}
}