Beispiel #1
0
// receiverHtlcSpendTimeout constructs a valid witness allowing the sender of
// an HTLC to recover the pending funds after an absolute timeout in the
// scenario that the receiver of the HTLC broadcasts their version of the
// commitment transaction.
func receiverHtlcSpendTimeout(commitScript []byte, outputAmt btcutil.Amount,
	senderKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	absoluteTimeout uint32) (wire.TxWitness, error) {

	// The HTLC output has an absolute time period before we are permitted
	// to recover the pending funds. Therefore we need to set the locktime
	// on this sweeping transaction in order to pass Script verification.
	sweepTx.LockTime = absoluteTimeout

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, senderKey)
	if err != nil {
		return nil, err
	}

	witnessStack := wire.TxWitness(make([][]byte, 4))
	witnessStack[0] = sweepSig
	witnessStack[1] = []byte{0}
	witnessStack[2] = []byte{0}
	witnessStack[3] = commitScript

	return witnessStack, nil
}
Beispiel #2
0
// receiverHtlcSpendRevoke constructs a valid witness allowing the sender of an
// HTLC within a previously revoked commitment transaction to re-claim the
// pending funds in the case that the receiver broadcasts this revoked
// commitment transaction.
func receiverHtlcSpendRevoke(commitScript []byte, outputAmt btcutil.Amount,
	senderKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	revokePreimage []byte) (wire.TxWitness, error) {

	// TODO(roasbeef): move sig generate outside func, or just factor out?
	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, senderKey)
	if err != nil {
		return nil, err
	}

	// We place a zero, then one as the first items in the evaluated
	// witness stack in order to force script execution to the HTLC
	// revocation clause.
	witnessStack := wire.TxWitness(make([][]byte, 5))
	witnessStack[0] = sweepSig
	witnessStack[1] = revokePreimage
	witnessStack[2] = []byte{1}
	witnessStack[3] = []byte{0}
	witnessStack[4] = commitScript

	return witnessStack, nil
}
Beispiel #3
0
// receiverHtlcSpendRedeem constructs a valid witness allowing the receiver of
// an HTLC to redeem the conditional payment in the event that their commitment
// transaction is broadcast. Since this is a pay out to the receiving party as
// an output on their commitment transaction, a relative time delay is required
// before the output can be spent.
func receiverHtlcSpendRedeem(commitScript []byte, outputAmt btcutil.Amount,
	reciverKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	paymentPreimage []byte, relativeTimeout uint32) (wire.TxWitness, error) {

	// In order to properly spend the transaction, we need to set the
	// sequence number. We do this by convering the relative block delay
	// into a sequence number value able to be interpeted by
	// OP_CHECKSEQUENCEVERIFY.
	sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, relativeTimeout)

	// Additionally, OP_CSV requires that the version of the transaction
	// spending a pkscript with OP_CSV within it *must* be >= 2.
	sweepTx.Version = 2

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, reciverKey)
	if err != nil {
		return nil, err
	}

	// Place a one as the first item in the evaluated witness stack to
	// force script execution to the HTLC redemption clause.
	witnessStack := wire.TxWitness(make([][]byte, 4))
	witnessStack[0] = sweepSig
	witnessStack[1] = paymentPreimage
	witnessStack[2] = []byte{1}
	witnessStack[3] = commitScript

	return witnessStack, nil
}
Beispiel #4
0
// signFundingTx generates signatures for all the inputs in the funding tx
// belonging to Bob.
// NOTE: This generates the full witness stack.
func (b *bobNode) signFundingTx(fundingTx *wire.MsgTx) ([]*lnwallet.InputScript, error) {
	bobInputScripts := make([]*lnwallet.InputScript, 0, len(b.availableOutputs))
	bobPkScript := b.changeOutputs[0].PkScript

	inputValue := int64(7e8)
	hashCache := txscript.NewTxSigHashes(fundingTx)
	for i, _ := range fundingTx.TxIn {
		// Alice has already signed this input.
		if fundingTx.TxIn[i].Witness != nil {
			continue
		}

		witness, err := txscript.WitnessScript(fundingTx, hashCache, i,
			inputValue, bobPkScript, txscript.SigHashAll, b.privKey,
			true)
		if err != nil {
			return nil, err
		}

		inputScript := &lnwallet.InputScript{Witness: witness}
		bobInputScripts = append(bobInputScripts, inputScript)
	}

	return bobInputScripts, nil
}
Beispiel #5
0
// signCommitTx generates a raw signature required for generating a spend from
// the funding transaction.
func (b *bobNode) signCommitTx(commitTx *wire.MsgTx, fundingScript []byte,
	channelValue int64) ([]byte, error) {

	hashCache := txscript.NewTxSigHashes(commitTx)

	return txscript.RawTxInWitnessSignature(commitTx, hashCache, 0,
		channelValue, fundingScript, txscript.SigHashAll, b.privKey)
}
Beispiel #6
0
// validateMsgTx verifies transaction input scripts for tx.  All previous output
// scripts from outputs redeemed by the transaction, in the same order they are
// spent, must be passed in the prevScripts slice.
func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte, inputValues []btcutil.Amount) error {
	hashCache := txscript.NewTxSigHashes(tx)
	for i, prevScript := range prevScripts {
		vm, err := txscript.NewEngine(prevScript, tx, i,
			txscript.StandardVerifyFlags, nil, hashCache, int64(inputValues[i]))
		if err != nil {
			return fmt.Errorf("cannot create script engine: %s", err)
		}
		err = vm.Execute()
		if err != nil {
			return fmt.Errorf("cannot validate transaction: %s", err)
		}
	}
	return nil
}
Beispiel #7
0
// commitSpendNoDelay constructs a valid witness allowing a node to spend their
// settled no-delay output on the counter-party's commitment transaction.
func commitSpendNoDelay(commitScript []byte, outputAmt btcutil.Amount,
	commitPriv *btcec.PrivateKey, sweepTx *wire.MsgTx) (wire.TxWitness, error) {

	// This is just a regular p2wkh spend which looks something like:
	//  * witness: <sig> <pubkey>
	hashCache := txscript.NewTxSigHashes(sweepTx)
	witness, err := txscript.WitnessScript(sweepTx, hashCache, 0,
		int64(outputAmt), commitScript, txscript.SigHashAll,
		commitPriv, true)
	if err != nil {
		return nil, err
	}

	return wire.TxWitness(witness), nil
}
Beispiel #8
0
// AddAllInputScripts modifies transaction a transaction by adding inputs
// scripts for each input.  Previous output scripts being redeemed by each input
// are passed in prevPkScripts and the slice length must match the number of
// inputs.  Private keys and redeem scripts are looked up using a SecretsSource
// based on the previous output script.
func AddAllInputScripts(tx *wire.MsgTx, prevPkScripts [][]byte, inputValues []btcutil.Amount,
	secrets SecretsSource) error {

	inputs := tx.TxIn
	hashCache := txscript.NewTxSigHashes(tx)
	chainParams := secrets.ChainParams()

	if len(inputs) != len(prevPkScripts) {
		return errors.New("tx.TxIn and prevPkScripts slices must " +
			"have equal length")
	}

	for i := range inputs {
		pkScript := prevPkScripts[i]

		switch {
		// If this is a p2sh output, who's script hash pre-image is a
		// witness program, then we'll need to use a modified signing
		// function which generates both the sigScript, and the witness
		// script.
		case txscript.IsPayToScriptHash(pkScript):
			err := spendNestedWitnessPubKeyHash(inputs[i], pkScript,
				int64(inputValues[i]), chainParams, secrets,
				tx, hashCache, i)
			if err != nil {
				return err
			}
		case txscript.IsPayToWitnessPubKeyHash(pkScript):
			err := spendWitnessKeyHash(inputs[i], pkScript,
				int64(inputValues[i]), chainParams, secrets,
				tx, hashCache, i)
			if err != nil {
				return err
			}
		default:
			sigScript := inputs[i].SignatureScript
			script, err := txscript.SignTxOutput(chainParams, tx, i,
				pkScript, txscript.SigHashAll, secrets, secrets,
				sigScript)
			if err != nil {
				return err
			}
			inputs[i].SignatureScript = script
		}
	}

	return nil
}
Beispiel #9
0
// commitSpendRevoke constructs a valid witness allowing a node to sweep the
// settled output of a malicious counter-party who broadcasts a revoked
// commitment trransaction.
func commitSpendRevoke(commitScript []byte, outputAmt btcutil.Amount,
	revocationPriv *btcec.PrivateKey, sweepTx *wire.MsgTx) (wire.TxWitness, error) {

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, revocationPriv)
	if err != nil {
		return nil, err
	}

	// Place a 1 as the first item in the evaluated witness stack to
	// force script execution to the revocation clause.
	witnessStack := wire.TxWitness(make([][]byte, 3))
	witnessStack[0] = sweepSig
	witnessStack[1] = []byte{1}
	witnessStack[2] = commitScript

	return witnessStack, nil
}
Beispiel #10
0
// htlcSpendTimeout constructs a valid witness allowing the sender of an HTLC
// to recover the pending funds after an absolute, then relative locktime
// period.
func senderHtlcSpendTimeout(commitScript []byte, outputAmt btcutil.Amount,
	senderKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	absoluteTimeout, relativeTimeout uint32) (wire.TxWitness, error) {

	// Since the HTLC output has an absolute timeout before we're permitted
	// to sweep the output, we need to set the locktime of this sweepign
	// transaction to that aboslute value in order to pass Script
	// verification.
	sweepTx.LockTime = absoluteTimeout

	// Additionally, we're required to wait a relative period of time
	// before we can sweep the output in order to allow the other party to
	// contest our claim of validity to this version of the commitment
	// transaction.
	sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, relativeTimeout)

	// Finally, OP_CSV requires that the version of the transaction
	// spending a pkscript with OP_CSV within it *must* be >= 2.
	sweepTx.Version = 2

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, senderKey)
	if err != nil {
		return nil, err
	}

	// We place a zero as the first item of the evaluated witness stack in
	// order to force Script execution to the HTLC timeout clause.
	witnessStack := wire.TxWitness(make([][]byte, 3))
	witnessStack[0] = sweepSig
	witnessStack[1] = []byte{0}
	witnessStack[2] = commitScript

	return witnessStack, nil
}
Beispiel #11
0
// senderHtlcSpendRevoke constructs a valid witness allowing the reciever of an
// HTLC to claim the output with knowledge of the revocation preimage in the
// scenario that the sender of the HTLC broadcasts a previously revoked
// commitment transaction. A valid spend requires knowledge of the pre-image to
// the commitment transaction's revocation hash, and a valid signature under
// the receiver's public key.
func senderHtlcSpendRevoke(commitScript []byte, outputAmt btcutil.Amount,
	reciverKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	revokePreimage []byte) (wire.TxWitness, error) {

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, reciverKey)
	if err != nil {
		return nil, err
	}

	// In order to force script execution to enter the revocation clause,
	// we place two one's as the first items in the final evalulated
	// witness stack.
	witnessStack := wire.TxWitness(make([][]byte, 5))
	witnessStack[0] = sweepSig
	witnessStack[1] = revokePreimage
	witnessStack[2] = []byte{1}
	witnessStack[3] = []byte{1}
	witnessStack[4] = commitScript

	return witnessStack, nil
}
Beispiel #12
0
// senderHtlcSpendRedeem constructs a valid witness allowing the receiver of an
// HTLC to redeem the pending output in the scenario that the sender broadcasts
// their version of the commitment transaction. A valid spend requires
// knowledge of the payment pre-image, and a valid signature under the
// receivers public key.
func senderHtlcSpendRedeem(commitScript []byte, outputAmt btcutil.Amount,
	reciverKey *btcec.PrivateKey, sweepTx *wire.MsgTx,
	paymentPreimage []byte) (wire.TxWitness, error) {

	hashCache := txscript.NewTxSigHashes(sweepTx)
	sweepSig, err := txscript.RawTxInWitnessSignature(
		sweepTx, hashCache, 0, int64(outputAmt), commitScript,
		txscript.SigHashAll, reciverKey)
	if err != nil {
		return nil, err
	}

	// We force script execution into the HTLC redemption clause by placing
	// a one, then a zero as the first items in the final evalulated
	// witness stack.
	witnessStack := wire.TxWitness(make([][]byte, 5))
	witnessStack[0] = sweepSig
	witnessStack[1] = paymentPreimage
	witnessStack[2] = []byte{0}
	witnessStack[3] = []byte{1}
	witnessStack[4] = commitScript

	return witnessStack, nil
}
Beispiel #13
0
// handleFundingCounterPartySigs is the final step in the channel reservation
// workflow. During this step, we validate *all* the received signatures for
// inputs to the funding transaction. If any of these are invalid, we bail,
// and forcibly cancel this funding request. Additionally, we ensure that the
// signature we received from the counterparty for our version of the commitment
// transaction allows us to spend from the funding output with the addition of
// our signature.
func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigsMsg) {
	l.limboMtx.RLock()
	res, ok := l.fundingLimbo[msg.pendingFundingID]
	l.limboMtx.RUnlock()
	if !ok {
		msg.err <- fmt.Errorf("attempted to update non-existant funding state")
		return
	}

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

	// Now we can complete the funding transaction by adding their
	// signatures to their inputs.
	res.theirFundingInputScripts = msg.theirFundingInputScripts
	inputScripts := msg.theirFundingInputScripts
	fundingTx := res.fundingTx
	sigIndex := 0
	fundingHashCache := txscript.NewTxSigHashes(fundingTx)
	for i, txin := range fundingTx.TxIn {
		if len(inputScripts) != 0 && len(txin.Witness) == 0 {
			// Attach the input scripts so we can verify it below.
			txin.Witness = inputScripts[sigIndex].Witness
			txin.SignatureScript = inputScripts[sigIndex].ScriptSig

			// Fetch the alleged previous output along with the
			// pkscript referenced by this input.
			prevOut := txin.PreviousOutPoint
			output, err := l.chainIO.GetUtxo(&prevOut.Hash, prevOut.Index)
			if output == nil {
				msg.err <- fmt.Errorf("input to funding tx does not exist: %v", err)
				return
			}

			// Ensure that the witness+sigScript combo is valid.
			vm, err := txscript.NewEngine(output.PkScript,
				fundingTx, i, txscript.StandardVerifyFlags, nil,
				fundingHashCache, output.Value)
			if err != nil {
				// TODO(roasbeef): cancel at this stage if invalid sigs?
				msg.err <- fmt.Errorf("cannot create script engine: %s", err)
				return
			}
			if err = vm.Execute(); err != nil {
				msg.err <- fmt.Errorf("cannot validate transaction: %s", err)
				return
			}

			sigIndex++
		}
	}

	// At this point, we can also record and verify their signature for our
	// commitment transaction.
	res.theirCommitmentSig = msg.theirCommitmentSig
	commitTx := res.partialState.OurCommitTx
	theirKey := res.theirContribution.MultiSigKey

	// Re-generate both the witnessScript and p2sh output. We sign the
	// witnessScript script, but include the p2sh output as the subscript
	// for verification.
	witnessScript := res.partialState.FundingWitnessScript

	// Next, create the spending scriptSig, and then verify that the script
	// is complete, allowing us to spend from the funding transaction.
	theirCommitSig := msg.theirCommitmentSig
	channelValue := int64(res.partialState.Capacity)
	hashCache := txscript.NewTxSigHashes(commitTx)
	sigHash, err := txscript.CalcWitnessSigHash(witnessScript, hashCache,
		txscript.SigHashAll, commitTx, 0, channelValue)
	if err != nil {
		msg.err <- fmt.Errorf("counterparty's commitment signature is invalid: %v", 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(theirCommitSig, btcec.S256())
	if err != nil {
		msg.err <- err
		return
	} else if !sig.Verify(sigHash, theirKey) {
		msg.err <- fmt.Errorf("counterparty's commitment signature is invalid")
		return
	}
	res.partialState.OurCommitSig = theirCommitSig

	// Funding complete, this entry can be removed from limbo.
	l.limboMtx.Lock()
	delete(l.fundingLimbo, res.reservationID)
	l.limboMtx.Unlock()

	walletLog.Infof("Broadcasting funding tx for ChannelPoint(%v): %v",
		res.partialState.FundingOutpoint, spew.Sdump(fundingTx))

	// Broacast the finalized funding transaction to the network.
	if err := l.PublishTransaction(fundingTx); err != nil {
		msg.err <- err
		return
	}

	// Add the complete funding transaction to the DB, in it's open bucket
	// which will be used for the lifetime of this channel.
	// TODO(roasbeef): revisit faul-tolerance of this flow
	nodeAddr := res.nodeAddr
	if err := res.partialState.FullSyncWithAddr(nodeAddr); err != nil {
		msg.err <- err
		return
	}

	// Create a goroutine to watch the chain so we can open the channel once
	// the funding tx has enough confirmations.
	go l.openChannelAfterConfirmations(res)

	msg.err <- nil
}
Beispiel #14
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
}
Beispiel #15
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
}
Beispiel #16
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
}
Beispiel #17
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)
}
// 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)
	}
}
Beispiel #19
0
// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block using multiple goroutines.
func checkBlockScripts(block *btcutil.Block, utxoView *UtxoViewpoint,
	scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache,
	hashCache *txscript.HashCache) error {

	// First determine if segwit is active according to the scriptFlags. If
	// it isn't then we don't need to interact with the HashCache.
	segwitActive := scriptFlags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness

	// Collect all of the transaction inputs and required information for
	// validation for all transactions in the block into a single slice.
	numInputs := 0
	for _, tx := range block.Transactions() {
		numInputs += len(tx.MsgTx().TxIn)
	}
	txValItems := make([]*txValidateItem, 0, numInputs)
	for _, tx := range block.Transactions() {
		sha := tx.Hash()

		// If the HashCache is present, and it doesn't yet contain the
		// partial sighashes for this transaction, then we add the
		// sighashes for the transaction. This allows us to take
		// advantage of the potential speed savings due to the new
		// digest algorithm (BIP0143).
		if segwitActive && hashCache != nil &&
			!hashCache.ContainsHashes(sha) {
			hashCache.AddSigHashes(tx.MsgTx())
		}

		var cachedHashes *txscript.TxSigHashes
		if segwitActive {
			if hashCache != nil {
				cachedHashes, _ = hashCache.GetSigHashes(sha)
			} else {
				cachedHashes = txscript.NewTxSigHashes(tx.MsgTx())
			}
		}

		for txInIdx, txIn := range tx.MsgTx().TxIn {
			// Skip coinbases.
			if txIn.PreviousOutPoint.Index == math.MaxUint32 {
				continue
			}

			txVI := &txValidateItem{
				txInIndex: txInIdx,
				txIn:      txIn,
				tx:        tx,
				sigHashes: cachedHashes,
			}
			txValItems = append(txValItems, txVI)
		}
	}

	// Validate all of the inputs.
	validator := newTxValidator(utxoView, scriptFlags, sigCache, hashCache)
	start := time.Now()
	if err := validator.Validate(txValItems); err != nil {
		return err
	}
	elapsed := time.Since(start)

	log.Tracef("block %v took %v to verify", block.Hash(), elapsed)

	// If the HashCache is present, once we have validated the block, we no
	// longer need the cached hashes for these transactions, so we purge
	// them from the cache.
	if segwitActive && hashCache != nil {
		for _, tx := range block.Transactions() {
			hashCache.PurgeSigHashes(tx.Hash())
		}
	}

	return nil
}
Beispiel #20
-1
// createSweepTx creates a final sweeping transaction with all witnesses
// inplace for all inputs. The created transaction has a single output sending
// all the funds back to the source wallet.
func (u *utxoNursery) createSweepTx(matureOutputs []*immatureOutput) (*wire.MsgTx, error) {
	sweepAddr, err := u.wallet.NewAddress(lnwallet.WitnessPubKey, false)
	if err != nil {
		return nil, err
	}
	pkScript, err := txscript.PayToAddrScript(sweepAddr)
	if err != nil {
		return nil, err
	}

	var totalSum btcutil.Amount
	for _, o := range matureOutputs {
		totalSum += o.amt
	}

	sweepTx := wire.NewMsgTx()
	sweepTx.Version = 2
	sweepTx.AddTxOut(&wire.TxOut{
		PkScript: pkScript,
		Value:    int64(totalSum - 1000),
	})
	for _, utxo := range matureOutputs {
		sweepTx.AddTxIn(&wire.TxIn{
			PreviousOutPoint: utxo.outPoint,
			// TODO(roasbeef): assumes pure block delays
			Sequence: utxo.blocksToMaturity,
		})
	}

	// TODO(roasbeef): insert fee calculation
	//  * remove hardcoded fee above

	// With all the inputs in place, use each output's unique witness
	// function to generate the final witness required for spending.
	hashCache := txscript.NewTxSigHashes(sweepTx)
	for i, txIn := range sweepTx.TxIn {
		witness, err := matureOutputs[i].witnessFunc(sweepTx, hashCache, i)
		if err != nil {
			return nil, err
		}

		txIn.Witness = witness
	}

	return sweepTx, nil
}