コード例 #1
0
ファイル: withdrawal.go プロジェクト: justusranvier/btcwallet
// getRawSigs iterates over the inputs of each transaction given, constructing the
// raw signatures for them using the private keys available to us.
// It returns a map of ntxids to signature lists.
func getRawSigs(transactions []*withdrawalTx) (map[Ntxid]TxSigs, error) {
	sigs := make(map[Ntxid]TxSigs)
	for _, tx := range transactions {
		txSigs := make(TxSigs, len(tx.inputs))
		msgtx := tx.toMsgTx()
		ntxid := tx.ntxid()
		for inputIdx, input := range tx.inputs {
			creditAddr := input.addr
			redeemScript := creditAddr.redeemScript()
			series := creditAddr.series()
			// The order of the raw signatures in the signature script must match the
			// order of the public keys in the redeem script, so we sort the public keys
			// here using the same API used to sort them in the redeem script and use
			// series.getPrivKeyFor() to lookup the corresponding private keys.
			pubKeys, err := branchOrder(series.publicKeys, creditAddr.Branch())
			if err != nil {
				return nil, err
			}
			txInSigs := make([]RawSig, len(pubKeys))
			for i, pubKey := range pubKeys {
				var sig RawSig
				privKey, err := series.getPrivKeyFor(pubKey)
				if err != nil {
					return nil, err
				}
				if privKey != nil {
					childKey, err := privKey.Child(uint32(creditAddr.Index()))
					if err != nil {
						return nil, newError(ErrKeyChain, "failed to derive private key", err)
					}
					ecPrivKey, err := childKey.ECPrivKey()
					if err != nil {
						return nil, newError(ErrKeyChain, "failed to obtain ECPrivKey", err)
					}
					log.Debugf("Generating raw sig for input %d of tx %s with privkey of %s",
						inputIdx, ntxid, pubKey.String())
					sig, err = txscript.RawTxInSignature(
						msgtx, inputIdx, redeemScript, txscript.SigHashAll, ecPrivKey)
					if err != nil {
						return nil, newError(ErrRawSigning, "failed to generate raw signature", err)
					}
				} else {
					log.Debugf("Not generating raw sig for input %d of %s because private key "+
						"for %s is not available: %v", inputIdx, ntxid, pubKey.String(), err)
					sig = []byte{}
				}
				txInSigs[i] = sig
			}
			txSigs[inputIdx] = txInSigs
		}
		sigs[ntxid] = txSigs
	}
	return sigs, nil
}
コード例 #2
0
ファイル: channel.go プロジェクト: conseweb/lnd
// SignCounterPartyCommitment...
func (c *ChannelUpdate) SignCounterPartyCommitment() ([]byte, error) {
	c.lnChannel.stateMtx.RLock()
	defer c.lnChannel.stateMtx.RUnlock()

	if c.sigTheirNewCommit != nil {
		return c.sigTheirNewCommit, nil
	}

	// Sign their version of the commitment transaction.
	sig, err := txscript.RawTxInSignature(c.theirPendingCommitTx, 0,
		c.lnChannel.channelState.FundingRedeemScript, txscript.SigHashAll,
		c.lnChannel.channelState.MultiSigKey)
	if err != nil {
		return nil, err
	}

	c.sigTheirNewCommit = sig

	return sig, nil
}
コード例 #3
0
ファイル: wallet_test.go プロジェクト: martindale/lnd
// signFundingTx generates a raw signature required for generating a spend from
// the funding transaction.
func (b *bobNode) signCommitTx(commitTx *wire.MsgTx, fundingScript []byte) ([]byte, error) {
	return txscript.RawTxInSignature(commitTx, 0, fundingScript,
		txscript.SigHashAll, b.privKey)
}
コード例 #4
0
ファイル: wallet.go プロジェクト: mkl-/lnd
// handleFundingCounterPartySigs is the final step in the channel reservation
// workflow. During this setp, 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()
	pendingReservation, 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
	pendingReservation.Lock()
	defer pendingReservation.Unlock()

	// Now we can complete the funding transaction by adding their
	// signatures to their inputs.
	pendingReservation.theirFundingSigs = msg.theirFundingSigs
	fundingTx := pendingReservation.partialState.FundingTx
	for i, txin := range fundingTx.TxIn {
		if txin.SignatureScript == nil {
			// Fetch the alleged previous output along with the
			// pkscript referenced by this input.
			prevOut := txin.PreviousOutPoint
			output, err := l.rpc.GetTxOut(&prevOut.Hash, prevOut.Index, false)
			if output == nil {
				// TODO(roasbeef): do this at the start to avoid wasting out time?
				//  8 or a set of nodes "we" run with exposed unauthenticated RPC?
				msg.err <- fmt.Errorf("input to funding tx does not exist: %v", err)
				return
			}
			pkscript, err := hex.DecodeString(output.ScriptPubKey.Hex)
			if err != nil {
				msg.err <- err
				return
			}

			// Ensure that the signature is valid.
			vm, err := txscript.NewEngine(pkscript,
				fundingTx, i, txscript.StandardVerifyFlags, nil)
			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
			}

			txin.SignatureScript = pendingReservation.theirFundingSigs[i]
		}
	}

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

	// Re-generate both the redeemScript and p2sh output. We sign the
	// redeemScript script, but include the p2sh output as the subscript
	// for verification.
	redeemScript := pendingReservation.partialState.FundingRedeemScript
	p2sh, err := scriptHashPkScript(redeemScript)
	if err != nil {
		msg.err <- err
		return
	}

	// First, we sign our copy of the commitment transaction ourselves.
	ourCommitSig, err := txscript.RawTxInSignature(commitTx, 0, redeemScript,
		txscript.SigHashAll, ourKey)
	if err != nil {
		msg.err <- err
		return
	}

	// Next, create the spending scriptSig, and then verify that the script
	// is complete, allowing us to spend from the funding transaction.
	//
	// When initially generating the redeemScript, we sorted the serialized
	// public keys in descending order. So we do a quick comparison in order
	// ensure the signatures appear on the Script Virual Machine stack in
	// the correct order.
	var scriptSig []byte
	theirCommitSig := msg.theirCommitmentSig
	if bytes.Compare(ourKey.PubKey().SerializeCompressed(), theirKey.SerializeCompressed()) == -1 {
		scriptSig, err = spendMultiSig(redeemScript, theirCommitSig, ourCommitSig)
	} else {
		scriptSig, err = spendMultiSig(redeemScript, ourCommitSig, theirCommitSig)
	}
	if err != nil {
		msg.err <- err
		return
	}

	// Finally, create an instance of a Script VM, and ensure that the
	// Script executes succesfully.
	commitTx.TxIn[0].SignatureScript = scriptSig
	vm, err := txscript.NewEngine(p2sh, commitTx, 0,
		txscript.StandardVerifyFlags, nil)
	if err != nil {
		msg.err <- err
		return
	}
	if err := vm.Execute(); err != nil {
		msg.err <- fmt.Errorf("counterparty's commitment signature is invalid: %v", err)
		return
	}

	// Funding complete, this entry can be removed from limbo.
	l.limboMtx.Lock()
	delete(l.fundingLimbo, pendingReservation.reservationID)
	// TODO(roasbeef): unlock outputs here, Store.InsertTx will handle marking
	// input in unconfirmed tx, so future coin selects don't pick it up
	//  * also record location of change address so can use AddCredit
	l.limboMtx.Unlock()

	// Add the complete funding transaction to the DB, in it's open bucket
	// which will be used for the lifetime of this channel.
	err = l.ChannelDB.PutOpenChannel(pendingReservation.partialState)

	// Create a goroutine to watch the chain so we can open the channel once
	// the funding tx has enough confirmations.
	// TODO(roasbeef): add number of confs to the confi
	go l.openChannelAfterConfirmations(pendingReservation, 3)

	msg.err <- err
}
コード例 #5
0
ファイル: wallet.go プロジェクト: mkl-/lnd
// handleFundingCounterPartyFunds 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.partialState.FundingTx = wire.NewMsgTx()
	fundingTx := pendingReservation.partialState.FundingTx

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

	// First, add all multi-party inputs to the transaction
	// TODO(roasbeef); handle case that tx doesn't exist, fake input
	// TODO(roasbeef): validate SPV proof from other side if in SPV mode.
	//  * actually, pure SPV would need fraud proofs right? must prove input
	//    is unspent
	//  * or, something like getutxo?
	for _, ourInput := range ourContribution.Inputs {
		fundingTx.AddTxIn(ourInput)
	}
	for _, theirInput := range theirContribution.Inputs {
		fundingTx.AddTxIn(theirInput)
	}

	// Next, add all multi-party outputs to the transaction. This includes
	// change outputs for both side.
	for _, ourChangeOutput := range ourContribution.ChangeOutputs {
		fundingTx.AddTxOut(ourChangeOutput)
	}
	for _, theirChangeOutput := range theirContribution.ChangeOutputs {
		fundingTx.AddTxOut(theirChangeOutput)
	}

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

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

	// Register intent for notifications related to the funding output.
	// This'll allow us to properly track the number of confirmations the
	// funding tx has once it has been broadcasted.
	lastBlock := l.Manager.SyncedTo()
	scriptAddr, err := l.Manager.ImportScript(redeemScript, &lastBlock)
	if err != nil {
		req.err <- err
		return
	}
	if err := l.rpc.NotifyReceived([]btcutil.Address{scriptAddr.Address()}); err != nil {
		req.err <- err
		return
	}

	pendingReservation.partialState.FundingRedeemScript = redeemScript
	fundingTx.AddTxOut(multiSigOut)

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

	// Next, sign all inputs that are ours, collecting the signatures in
	// order of the inputs.
	pendingReservation.ourFundingSigs = make([][]byte, 0, len(ourContribution.Inputs))
	for i, txIn := range fundingTx.TxIn {
		// Does the wallet know about the txin?
		txDetail, _ := l.TxStore.TxDetails(&txIn.PreviousOutPoint.Hash)
		if txDetail == nil {
			continue
		}

		// Is this our txin? TODO(roasbeef): assumes all inputs are P2PKH...
		prevIndex := txIn.PreviousOutPoint.Index
		prevOut := txDetail.TxRecord.MsgTx.TxOut[prevIndex]
		_, addrs, _, _ := txscript.ExtractPkScriptAddrs(prevOut.PkScript, ActiveNetParams)
		apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
		if !ok {
			req.err <- btcwallet.ErrUnsupportedTransactionType
			return
		}

		ai, err := l.Manager.Address(apkh)
		if err != nil {
			req.err <- fmt.Errorf("cannot get address info: %v", err)
			return
		}
		pka := ai.(waddrmgr.ManagedPubKeyAddress)
		privkey, err := pka.PrivKey()
		if err != nil {
			req.err <- fmt.Errorf("cannot get private key: %v", err)
			return
		}

		sigscript, err := txscript.SignatureScript(pendingReservation.partialState.FundingTx, i,
			prevOut.PkScript, txscript.SigHashAll, privkey,
			ai.Compressed())
		if err != nil {
			req.err <- fmt.Errorf("cannot create sigscript: %s", err)
			return
		}

		fundingTx.TxIn[i].SignatureScript = sigscript
		pendingReservation.ourFundingSigs = append(pendingReservation.ourFundingSigs, sigscript)
	}

	// 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).
	pendingReservation.partialState.TheirShaChain = shachain.New()
	pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationHash

	// Grab the hash of the current pre-image in our chain, this is needed
	// for our commitment tx.
	// TODO(roasbeef): grab partial state above to avoid long attr chain
	ourCurrentRevokeHash := pendingReservation.ourContribution.RevocationHash

	// Create the txIn to our commitment transaction. In the process, we
	// need to locate the index of the multi-sig output on the funding tx
	// since the outputs are cannonically sorted.
	fundingNTxid := fundingTx.TxSha() // NOTE: assumes testnet-L
	_, multiSigIndex := findScriptOutputIndex(fundingTx, multiSigOut.PkScript)
	fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingNTxid, multiSigIndex), nil)

	// With the funding tx complete, create both commitment transactions.
	initialBalance := ourContribution.FundingAmount
	pendingReservation.fundingLockTime = theirContribution.CsvDelay
	ourCommitKey := ourContribution.CommitKey
	theirCommitKey := theirContribution.CommitKey
	ourCommitTx, err := createCommitTx(fundingTxIn, ourCommitKey, theirCommitKey,
		ourCurrentRevokeHash[:], theirContribution.CsvDelay,
		initialBalance, initialBalance)
	if err != nil {
		req.err <- err
		return
	}
	theirCommitTx, err := createCommitTx(fundingTxIn, theirCommitKey, ourCommitKey,
		theirContribution.RevocationHash[:], theirContribution.CsvDelay,
		initialBalance, initialBalance)
	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)

	// Record newly available information witin the open channel state.
	pendingReservation.partialState.CsvDelay = theirContribution.CsvDelay
	pendingReservation.partialState.TheirDeliveryAddress = theirContribution.DeliveryAddress
	pendingReservation.partialState.ChanID = fundingNTxid
	pendingReservation.partialState.TheirCommitKey = theirCommitKey
	pendingReservation.partialState.TheirCommitTx = theirCommitTx
	pendingReservation.partialState.OurCommitTx = ourCommitTx

	// Generate a signature for their version of the initial commitment
	// transaction.
	sigTheirCommit, err := txscript.RawTxInSignature(theirCommitTx, 0, redeemScript,
		txscript.SigHashAll, ourKey)
	if err != nil {
		req.err <- err
		return
	}
	pendingReservation.ourCommitmentSig = sigTheirCommit

	req.err <- nil
}
コード例 #6
0
ファイル: lnwire_test.go プロジェクト: PaulCapestany/lnd
	// 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), wire.NewTxIn(outpoint2, 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)
	sigStr2, _          = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig2privKey)
	commitSig2, _       = btcec.ParseSignature(sigStr2, btcec.S256())
	// Slice of Funding TX Sigs
	ptrFundingTXSigs = append(*new([]*btcec.Signature), commitSig1, commitSig2)