예제 #1
0
파일: channel.go 프로젝트: conseweb/lnd
// newLightningChannel...
func newLightningChannel(wallet *LightningWallet, events chainntnfs.ChainNotifier,
	chanDB *channeldb.DB, state *channeldb.OpenChannel) (*LightningChannel, error) {

	lc := &LightningChannel{
		lnwallet:           wallet,
		channelEvents:      events,
		channelState:       state,
		channelDB:          chanDB,
		updateTotem:        make(chan struct{}, 1),
		pendingPayments:    make(map[PaymentHash]*PaymentDescriptor),
		unfufilledPayments: make(map[PaymentHash]*PaymentRequest),
	}

	// TODO(roasbeef): do a NotifySpent for the funding input, and
	// NotifyReceived for all commitment outputs.

	// Populate the totem.
	lc.updateTotem <- struct{}{}

	fundingTxId := state.FundingTx.TxSha()
	fundingPkScript, err := scriptHashPkScript(state.FundingRedeemScript)
	if err != nil {
		return nil, err
	}
	_, multiSigIndex := findScriptOutputIndex(state.FundingTx, fundingPkScript)
	lc.fundingTxIn = wire.NewTxIn(wire.NewOutPoint(&fundingTxId, multiSigIndex), nil)
	lc.fundingP2SH = fundingPkScript

	return lc, nil
}
예제 #2
0
// createSpendTx generates a basic spending transaction given the passed
// signature and public key scripts.
func createSpendingTx(sigScript, pkScript []byte) *wire.MsgTx {
	coinbaseTx := wire.NewMsgTx(wire.TxVersion)

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

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

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

	return spendingTx
}
예제 #3
0
// createSpendTx generates a basic spending transaction given the passed
// signature and public key scripts.
func createSpendingTx(sigScript, pkScript []byte) *wire.MsgTx {
	coinbaseTx := wire.NewMsgTx()

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

	spendingTx := wire.NewMsgTx()
	coinbaseTxSha := coinbaseTx.TxSha()
	outPoint = wire.NewOutPoint(&coinbaseTxSha, 0)
	txIn = wire.NewTxIn(outPoint, sigScript)
	txOut = wire.NewTxOut(0, 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(wire.TxVersion)
	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
// 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() (*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.
	bobAddr, err := btcutil.NewAddressPubKey(privKey.PubKey().SerializeCompressed(),
		ActiveNetParams)
	if err != nil {
		return nil, err
	}
	bobAddrScript, err := txscript.PayToAddrScript(bobAddr.AddressPubKeyHash())
	if err != nil {
		return nil, err
	}
	prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0))
	// TODO(roasbeef): When the chain rpc is hooked in, assert bob's output
	// actually exists and it unspent in the chain.
	bobTxIn := wire.NewTxIn(prevOut, nil)

	// Using bobs priv key above, create a change address 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 [20]byte
	copy(revocation[:], bobsPrivKey)
	revocation[0] = 0xff

	// His ID is just as creative...
	var id [wire.HashSize]byte
	id[0] = 0xff

	return &bobNode{
		id:               id,
		privKey:          privKey,
		channelKey:       pubKey,
		deliveryAddress:  bobAddr,
		revocation:       revocation,
		delay:            5,
		availableOutputs: []*wire.TxIn{bobTxIn},
		changeOutputs:    []*wire.TxOut{bobChangeOutput},
	}, nil
}
예제 #8
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))

	return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn, [][]byte, error) {
		for currentTotal < target && len(eligible) != 0 {
			nextCredit := &eligible[0]
			eligible = eligible[1:]
			nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil)
			currentTotal += nextCredit.Amount
			currentInputs = append(currentInputs, nextInput)
			currentScripts = append(currentScripts, nextCredit.PkScript)
		}
		return currentTotal, currentInputs, currentScripts, nil
	}
}
예제 #9
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))

	// 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
			}
		}
	}
}
예제 #10
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.Hash(), 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[chainhash.Hash]struct{}
	}{
		{
			name: "new store",
			f: func(s *Store) (*Store, error) {
				return s, nil
			},
			bal:      0,
			unc:      0,
			unspents: map[wire.OutPoint]struct{}{},
			unmined:  map[chainhash.Hash]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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstRecvTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstRecvTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstRecvTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]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[chainhash.Hash]struct{}{
				*TstSpendingTx.Hash(): {},
			},
		},
		{
			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[chainhash.Hash]struct{}{
				*TstSpendingTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstSpendingTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Hash(),
					Index: 1,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstSpendingTx.Hash(): {},
			},
		},
		{
			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.Hash(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Hash(),
					Index: 1,
				}: {},
			},
			unmined: map[chainhash.Hash]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.Hash(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Hash(),
					Index: 1,
				}: {},
			},
			unmined: map[chainhash.Hash]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.Hash(),
					Index: 0,
				}: {},
				wire.OutPoint{
					Hash:  *TstSpendingTx.Hash(),
					Index: 1,
				}: {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstSpendingTx.Hash(): {},
			},
		},
		{
			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.Hash(), 0): {},
				*wire.NewOutPoint(TstSpendingTx.Hash(), 1): {},
			},
			unmined: map[chainhash.Hash]struct{}{
				*TstDoubleSpendTx.Hash(): {},
				*TstSpendingTx.Hash():    {},
			},
		},
		{
			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.Hash(), 0): {},
			},
			unmined: map[chainhash.Hash]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.TxHash()
			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)
		}

	}
}
예제 #11
0
// createTxIn pulls the outpoint out of the funding TxOut and uses it as a reference
// for the txin that will be placed in a new transaction.
func createTxIn(outpoint *wire.OutPoint) *wire.TxIn {
	// The second arg is the txin's signature script, which we are leaving empty
	// until the entire transaction is ready.
	txin := wire.NewTxIn(outpoint, []byte{})
	return txin
}
예제 #12
0
// InsertBlock inserts raw block and transaction data from a block into the
// database.  The first block inserted into the database will be treated as the
// genesis block.  Every subsequent block insert requires the referenced parent
// block to already exist.
func (db *LevelDb) InsertBlock(block *btcutil.Block) (height int64, rerr error) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()
	defer func() {
		if rerr == nil {
			rerr = db.processBatches()
		} else {
			db.lBatch().Reset()
		}
	}()

	blocksha := block.Sha()
	mblock := block.MsgBlock()
	rawMsg, err := block.Bytes()
	if err != nil {
		log.Warnf("Failed to obtain raw block sha %v", blocksha)
		return 0, err
	}
	txloc, err := block.TxLoc()
	if err != nil {
		log.Warnf("Failed to obtain raw block sha %v", blocksha)
		return 0, err
	}

	// Insert block into database
	newheight, err := db.insertBlockData(blocksha, &mblock.Header.PrevBlock,
		rawMsg)
	if err != nil {
		log.Warnf("Failed to insert block %v %v %v", blocksha,
			&mblock.Header.PrevBlock, err)
		return 0, err
	}

	// At least two blocks in the long past were generated by faulty
	// miners, the sha of the transaction exists in a previous block,
	// detect this condition and 'accept' the block.
	for txidx, tx := range mblock.Transactions {
		txsha, err := block.TxSha(txidx)
		if err != nil {
			log.Warnf("failed to compute tx name block %v idx %v err %v", blocksha, txidx, err)
			return 0, err
		}
		spentbuflen := (len(tx.TxOut) + 7) / 8
		spentbuf := make([]byte, spentbuflen, spentbuflen)
		if len(tx.TxOut)%8 != 0 {
			for i := uint(len(tx.TxOut) % 8); i < 8; i++ {
				spentbuf[spentbuflen-1] |= (byte(1) << i)
			}
		}

		err = db.insertTx(txsha, newheight, txloc[txidx].TxStart, txloc[txidx].TxLen, spentbuf)
		if err != nil {
			log.Warnf("block %v idx %v failed to insert tx %v %v err %v", blocksha, newheight, &txsha, txidx, err)
			return 0, err
		}

		// Some old blocks contain duplicate transactions
		// Attempt to cleanly bypass this problem by marking the
		// first as fully spent.
		// http://blockexplorer.com/b/91812 dup in 91842
		// http://blockexplorer.com/b/91722 dup in 91880
		if newheight == 91812 {
			dupsha, err := wire.NewShaHashFromStr("d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599")
			if err != nil {
				panic("invalid sha string in source")
			}
			if txsha.IsEqual(dupsha) {
				// marking TxOut[0] as spent
				po := wire.NewOutPoint(dupsha, 0)
				txI := wire.NewTxIn(po, []byte("garbage"))

				var spendtx wire.MsgTx
				spendtx.AddTxIn(txI)
				err = db.doSpend(&spendtx)
				if err != nil {
					log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err)
				}
			}
		}
		if newheight == 91722 {
			dupsha, err := wire.NewShaHashFromStr("e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468")
			if err != nil {
				panic("invalid sha string in source")
			}
			if txsha.IsEqual(dupsha) {
				// marking TxOut[0] as spent
				po := wire.NewOutPoint(dupsha, 0)
				txI := wire.NewTxIn(po, []byte("garbage"))

				var spendtx wire.MsgTx
				spendtx.AddTxIn(txI)
				err = db.doSpend(&spendtx)
				if err != nil {
					log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err)
				}
			}
		}

		err = db.doSpend(tx)
		if err != nil {
			log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, txsha, txidx, err)
			return 0, err
		}
	}
	return newheight, nil
}
예제 #13
0
파일: lnwire.go 프로젝트: martindale/lnd
func readElement(r io.Reader, element interface{}) error {
	var err error
	switch e := element.(type) {
	case *uint8:
		var b [1]uint8
		_, err = r.Read(b[:])
		if err != nil {
			return err
		}
		*e = b[0]
		return nil
	case *uint16:
		var b [2]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = binary.BigEndian.Uint16(b[:])
		return nil
	case *CreditsAmount:
		var b [4]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = CreditsAmount(int32(binary.BigEndian.Uint32(b[:])))
		return nil
	case *uint32:
		var b [4]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = binary.BigEndian.Uint32(b[:])
		return nil
	case *uint64:
		var b [8]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = binary.BigEndian.Uint64(b[:])
		return nil
	case *HTLCKey:
		var b [8]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = HTLCKey(binary.BigEndian.Uint64(b[:]))
		return nil
	case *btcutil.Amount:
		var b [8]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
		return nil
	case **wire.ShaHash:
		var b wire.ShaHash
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = &b
		return nil
	case **btcec.PublicKey:
		var b [33]byte
		_, err = io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		x, err := btcec.ParsePubKey(b[:], btcec.S256())
		if err != nil {
			return err
		}
		*e = &*x
		return nil
	case *[]uint64:
		var numItems uint16
		err = readElement(r, &numItems)
		if 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
		return nil
	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:
		var sigLength uint8
		err = readElement(r, &sigLength)
		if err != nil {
			return err
		}

		if sigLength > 73 {
			return fmt.Errorf("Signature too long!")
		}

		// Read the sig length
		l := io.LimitReader(r, int64(sigLength))
		sig, err := ioutil.ReadAll(l)
		if err != nil {
			return err
		}
		if len(sig) != int(sigLength) {
			return fmt.Errorf("EOF: Signature length mismatch.")
		}
		btcecSig, err := btcec.ParseSignature(sig, btcec.S256())
		if err != nil {
			return err
		}
		*e = &*btcecSig
		return nil
	case *[]*[20]byte:
		// How many to read
		var sliceSize uint16
		err = readElement(r, &sliceSize)
		if err != nil {
			return err
		}
		var data []*[20]byte
		// Append the actual
		for i := uint16(0); i < sliceSize; i++ {
			var element [20]byte
			err = readElement(r, &element)
			if err != nil {
				return err
			}
			data = append(data, &element)
		}
		*e = data
		return nil
	case *[20]byte:
		_, err = io.ReadFull(r, e[:])
		if err != nil {
			return err
		}
		return nil
	case *wire.BitcoinNet:
		var b [4]byte
		_, err := io.ReadFull(r, b[:])
		if err != nil {
			return err
		}
		*e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:]))
		return nil
	case *[]byte:
		// Get the blob length first
		var blobLength uint16
		err = readElement(r, &blobLength)
		if err != nil {
			return err
		}

		// Shouldn't need to do this, since it's uint16, but we
		// might have a different value for MAX_SLICE_LENGTH...
		if int(blobLength) > MAX_SLICE_LENGTH {
			return fmt.Errorf("Slice length too long!")
		}

		// Read the slice length
		l := io.LimitReader(r, int64(blobLength))
		*e, err = ioutil.ReadAll(l)
		if err != nil {
			return err
		}
		if len(*e) != int(blobLength) {
			return fmt.Errorf("EOF: Slice length mismatch.")
		}
		return nil
	case *PkScript:
		// Get the script length first
		var scriptLength uint8
		err = readElement(r, &scriptLength)
		if err != nil {
			return err
		}

		if scriptLength > 25 {
			return fmt.Errorf("PkScript too long!")
		}

		// Read the script length
		l := io.LimitReader(r, int64(scriptLength))
		*e, err = ioutil.ReadAll(l)
		if err != nil {
			return err
		}
		if len(*e) != int(scriptLength) {
			return fmt.Errorf("EOF: Signature length mismatch.")
		}
		return nil
	case *string:
		// Get the string length first
		var strlen uint16
		err = readElement(r, &strlen)
		if err != nil {
			return err
		}
		// Read the string for the length
		l := io.LimitReader(r, int64(strlen))
		b, err := ioutil.ReadAll(l)
		if len(b) != int(strlen) {
			return fmt.Errorf("EOF: String length mismatch.")
		}
		*e = string(b)
		if err != nil {
			return err
		}
		return nil
	case *[]*wire.TxIn:
		// Read the size (1-byte number of txins)
		var numScripts uint8
		err = readElement(r, &numScripts)
		if err != nil {
			return err
		}
		if numScripts > 127 {
			return fmt.Errorf("Too many txins")
		}

		// Append the actual TxIns
		var txins []*wire.TxIn
		for i := uint8(0); i < numScripts; i++ {
			outpoint := new(wire.OutPoint)
			txin := wire.NewTxIn(outpoint, nil)
			err = readElement(r, &txin)
			if err != nil {
				return err
			}
			txins = append(txins, txin)
		}
		*e = *&txins
		return nil
	case **wire.TxIn:
		// Hash
		var h [32]byte
		_, err = io.ReadFull(r, h[:])
		if 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
	default:
		return fmt.Errorf("Unknown type in readElement: %T", e)
	}

	return nil
}
예제 #14
0
파일: wallet_test.go 프로젝트: conseweb/lnd
// 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) (*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.
	bobAddrPk, err := btcutil.NewAddressPubKey(privKey.PubKey().SerializeCompressed(),
		miner.ActiveNet)
	if err != nil {
		return nil, err
	}
	bobAddr := bobAddrPk.AddressPubKeyHash()
	bobAddrScript, err := txscript.PayToAddrScript(bobAddr)
	if err != nil {
		return nil, err
	}

	// Give bobNode one 7 BTC output for use in creating channels.
	outputMap := map[string]btcutil.Amount{
		bobAddr.String(): btcutil.Amount(7e8),
	}
	mainTxid, err := miner.CoinbaseSpend(outputMap)
	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 := findScriptOutputIndex(tx.MsgTx(), bobAddrScript)
	if !found {
		return nil, fmt.Errorf("output to bob never created")
	}

	prevOut := wire.NewOutPoint(mainTxid, index)
	// TODO(roasbeef): When the chain rpc is hooked in, assert bob's output
	// actually exists and it unspent in the chain.
	bobTxIn := wire.NewTxIn(prevOut, 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 [20]byte
	copy(revocation[:], bobsPrivKey)
	revocation[0] = 0xff

	// His ID is just as creative...
	var id [wire.HashSize]byte
	id[0] = 0xff

	return &bobNode{
		id:               id,
		privKey:          privKey,
		channelKey:       pubKey,
		deliveryAddress:  bobAddr,
		revocation:       revocation,
		delay:            5,
		availableOutputs: []*wire.TxIn{bobTxIn},
		changeOutputs:    []*wire.TxOut{bobChangeOutput},
	}, nil
}
예제 #15
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), 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)
예제 #16
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))
		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")
}
예제 #17
0
파일: main.go 프로젝트: libreoscar/btcwatch
func createTx(inputs *selectInputsResult, addr btcutil.Address, amount float64, change float64, msg []byte) *wire.MsgTx {
	tx := wire.NewMsgTx()
	txIns := make([]*wire.TxIn, len(inputs.inputs))
	for i, input := range inputs.inputs {
		hash, err := wire.NewShaHashFromStr(input.TxID)
		if err != nil {
			logger.Crit("invalid txid")
			os.Exit(0)
		}
		prevOut := wire.NewOutPoint(hash, input.Vout)
		txIn := wire.NewTxIn(prevOut, nil)
		txIns[i] = txIn
	}

	txOuts := make([]*wire.TxOut, 3)

	result, _ := client.ValidateAddress(addr)
	if !result.IsValid {
		logger.Crit("invalid address")
		os.Exit(0)
	}
	addrPkScript := result.ScriptPubKey
	addrPkScriptBin, _ := hex.DecodeString(addrPkScript)
	txOut := wire.NewTxOut(toSatoshi(amount), addrPkScriptBin)
	txOuts[0] = txOut

	changePkScript := inputs.inputs[0].ScriptPubKey
	changePKScriptBin, _ := hex.DecodeString(changePkScript)
	txOut = wire.NewTxOut(toSatoshi(change), changePKScriptBin)
	txOuts[1] = txOut

	msgLen := len(msg)

	var payload bytes.Buffer
	payload.WriteByte(0x6a)
	if msgLen <= 75 {
		payload.WriteByte(byte(msgLen))
	} else if msgLen <= 256 {
		payload.WriteByte(0x4c)
		payload.WriteByte(byte(msgLen))
	} else {
		payload.WriteByte(0x4d)
		payload.WriteByte(byte(msgLen % 256))
		payload.WriteByte(byte(msgLen / 256))
	}
	payload.Write(msg)
	opReturnBin := payload.Bytes()

	txOut = wire.NewTxOut(0, opReturnBin)
	txOuts[2] = txOut

	tx.TxIn = txIns
	tx.TxOut = txOuts

	var buf bytes.Buffer
	tx.Serialize(&buf)
	logger.Info("Created Tx:")
	logger.Info(spew.Sdump(hex.EncodeToString(buf.Bytes())))

	return tx
}
예제 #18
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()
	prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0))
	txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0})
	originTx.AddTxIn(txIn)
	pkScript, err := txscript.PayToAddrScript(addr)
	if err != nil {
		fmt.Println(err)
		return
	}
	txOut := wire.NewTxOut(100000000, pkScript)
	originTx.AddTxOut(txOut)

	originTxHash, err := originTx.TxSha()
	if err != nil {
		fmt.Println(err)
		return
	}

	// Create the transaction to redeem the fake transaction.
	redeemTx := wire.NewMsgTx()

	// 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)
	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
	s, err := txscript.NewScript(redeemTx.TxIn[0].SignatureScript,
		originTx.TxOut[0].PkScript, 0, redeemTx, flags)
	if err != nil {
		fmt.Println(err)
		return
	}
	if err := s.Execute(); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Transaction successfully signed")

	// Output:
	// Transaction successfully signed
}
예제 #19
0
func loadTestCredits(w *LightningWallet, numOutputs, btcPerOutput int) error {
	// Import the priv key (converting to WIF) above that controls all our
	// available outputs.
	privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey)
	if err := w.Unlock(privPass, time.Duration(0)); err != nil {
		return err
	}
	bs := &waddrmgr.BlockStamp{Hash: *genBlockHash(1), Height: 1}
	wif, err := btcutil.NewWIF(privKey, ActiveNetParams, true)
	if err != nil {
		return err
	}
	if _, err := w.ImportPrivateKey(wif, bs, false); err != nil {
		return nil
	}
	if err := w.Manager.SetSyncedTo(&waddrmgr.BlockStamp{int32(1), *genBlockHash(1)}); err != nil {
		return err
	}

	blk := wtxmgr.BlockMeta{wtxmgr.Block{Hash: *genBlockHash(2), Height: 2}, time.Now()}

	// Create a simple P2PKH pubkey script spendable by Alice. For simplicity
	// all of Alice's spendable funds will reside in this output.
	satosihPerOutput := int64(btcPerOutput * 1e8)
	walletAddr, err := btcutil.NewAddressPubKey(privKey.PubKey().SerializeCompressed(),
		ActiveNetParams)
	if err != nil {
		return err
	}
	walletScriptCredit, err := txscript.PayToAddrScript(walletAddr.AddressPubKeyHash())
	if err != nil {
		return err
	}

	// Create numOutputs outputs spendable by our wallet each holding btcPerOutput
	// in satoshis.
	tx := wire.NewMsgTx()
	prevOut := wire.NewOutPoint(genBlockHash(999), 1)
	txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0})
	tx.AddTxIn(txIn)
	for i := 0; i < numOutputs; i++ {
		tx.AddTxOut(wire.NewTxOut(satosihPerOutput, walletScriptCredit))
	}
	txCredit, err := wtxmgr.NewTxRecordFromMsgTx(tx, time.Now())
	if err != nil {
		return err
	}

	if err := addTestTx(w, txCredit, &blk); err != nil {
		return err
	}
	if err := w.Manager.SetSyncedTo(&waddrmgr.BlockStamp{int32(2), *genBlockHash(2)}); err != nil {
		return err
	}

	// Make the wallet think it's been synced to block 10. This way the
	// outputs we added above will have sufficient confirmations
	// (hard coded to 6 atm).
	for i := 3; i < 10; i++ {
		sha := *genBlockHash(i)
		if err := w.Manager.SetSyncedTo(&waddrmgr.BlockStamp{int32(i), sha}); err != nil {
			return err
		}
	}

	return nil
}
예제 #20
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
}
예제 #21
0
파일: wallet.go 프로젝트: mkl-/lnd
// handleFundingReserveRequest processes a message intending to create, and
// validate a funding reservation request.
func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg) {
	// Create a limbo and record entry for this newly pending funding request.
	l.limboMtx.Lock()

	id := l.nextFundingID
	reservation := newChannelReservation(req.fundingType, req.fundingAmount, req.minFeeRate, l, id)
	l.nextFundingID++
	l.fundingLimbo[id] = reservation

	l.limboMtx.Unlock()

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

	reservation.partialState.TheirLNID = req.nodeID
	ourContribution := reservation.ourContribution
	ourContribution.CsvDelay = req.csvDelay

	// We hold the coin select mutex while querying for outputs, and
	// performing coin selection in order to avoid inadvertent double spends
	// accross funding transactions.
	// NOTE: we don't use defer her so we can properly release the lock
	// when we encounter an error condition.
	l.coinSelectMtx.Lock()

	// Find all unlocked unspent outputs with greater than 6 confirmations.
	maxConfs := int32(math.MaxInt32)
	// TODO(roasbeef): make 6 a config paramter?
	unspentOutputs, err := l.ListUnspent(6, maxConfs, nil)
	if err != nil {
		l.coinSelectMtx.Unlock()
		req.err <- err
		req.resp <- nil
		return
	}

	// Convert the outputs to coins for coin selection below.
	coins, err := outputsToCoins(unspentOutputs)
	if err != nil {
		l.coinSelectMtx.Unlock()
		req.err <- err
		req.resp <- nil
		return
	}

	// Peform coin selection over our available, unlocked unspent outputs
	// in order to find enough coins to meet the funding amount requirements.
	//
	// TODO(roasbeef): Should extend coinset with optimal coin selection
	// heuristics for our use case.
	// TODO(roasbeef): factor in fees..
	// TODO(roasbeef): possibly integrate the fee prediction project? if
	// results hold up...
	// NOTE: this current selection assumes "priority" is still a thing.
	selector := &coinset.MaxValueAgeCoinSelector{
		MaxInputs:       10,
		MinChangeAmount: 10000,
	}
	selectedCoins, err := selector.CoinSelect(req.fundingAmount, coins)
	if err != nil {
		l.coinSelectMtx.Unlock()
		req.err <- err
		req.resp <- nil
		return
	}

	// 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.
	ourContribution.Inputs = make([]*wire.TxIn, len(selectedCoins.Coins()))
	for i, coin := range selectedCoins.Coins() {
		txout := wire.NewOutPoint(coin.Hash(), coin.Index())
		l.LockOutpoint(*txout)

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

	l.coinSelectMtx.Unlock()

	// Create some possibly neccessary change outputs.
	selectedTotalValue := coinset.NewCoinSet(selectedCoins.Coins()).TotalValue()
	if selectedTotalValue > req.fundingAmount {
		ourContribution.ChangeOutputs = make([]*wire.TxOut, 1)
		// Change is necessary. Query for an available change address to
		// send the remainder to.
		changeAddr, err := l.NewChangeAddress(waddrmgr.DefaultAccountNum)
		if err != nil {
			req.err <- err
			req.resp <- nil
			return
		}

		changeAddrScript, err := txscript.PayToAddrScript(changeAddr)
		if err != nil {
			req.err <- err
			req.resp <- nil
			return
		}

		changeAmount := selectedTotalValue - req.fundingAmount
		ourContribution.ChangeOutputs[0] = wire.NewTxOut(int64(changeAmount),
			changeAddrScript)
	}

	// TODO(roasbeef): re-calculate fees here to minFeePerKB, may need more inputs

	// Grab two fresh keys from out HD chain, one will be used for the
	// multi-sig funding transaction, and the other for the commitment
	// transaction.
	multiSigKey, err := l.getNextRawKey()
	if err != nil {
		req.err <- err
		req.resp <- nil
		return
	}
	commitKey, err := l.getNextRawKey()
	if err != nil {
		req.err <- err
		req.resp <- nil
		return
	}
	reservation.partialState.MultiSigKey = multiSigKey
	ourContribution.MultiSigKey = multiSigKey.PubKey()
	reservation.partialState.OurCommitKey = commitKey
	ourContribution.CommitKey = commitKey.PubKey()

	// Generate a fresh address to be used in the case of a cooperative
	// channel close.
	deliveryAddress, err := l.NewAddress(waddrmgr.DefaultAccountNum)
	if err != nil {
		req.err <- err
		req.resp <- nil
		return
	}
	reservation.partialState.OurDeliveryAddress = deliveryAddress
	ourContribution.DeliveryAddress = deliveryAddress

	// Create a new shaChain for verifiable transaction revocations. This
	// will be used to generate revocation hashes for our past/current
	// commitment transactions once we start to make payments within the
	// channel.
	shaChain, err := shachain.NewFromSeed(nil, 0)
	if err != nil {
		req.err <- err
		req.resp <- nil
		return
	}
	reservation.partialState.OurShaChain = shaChain
	copy(ourContribution.RevocationHash[:], shaChain.CurrentRevocationHash())

	// Funding reservation request succesfully handled. The funding inputs
	// will be marked as unavailable until the reservation is either
	// completed, or cancecled.
	req.resp <- reservation
	req.err <- nil
}
예제 #22
0
파일: shell.go 프로젝트: conseweb/lnd
// SendCoins does send coins, but it's very rudimentary
func SendCoins(s uspv.SPVCon, adr btcutil.Address, sendAmt int64) error {
	var err error
	var score int64
	allUtxos, err := s.TS.GetAllUtxos()
	if err != nil {
		return err
	}

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

	tx := wire.NewMsgTx() // make new tx
	// make address script 76a914...88ac
	adrScript, err := txscript.PayToAddrScript(adr)
	if err != nil {
		return err
	}
	// make user specified txout and add to tx
	txout := wire.NewTxOut(sendAmt, adrScript)
	tx.AddTxOut(txout)

	nokori := sendAmt // nokori is how much is needed on input side
	for _, utxo := range allUtxos {
		// generate pkscript to sign
		prevPKscript, err := txscript.PayToAddrScript(
			s.TS.Adrs[utxo.KeyIdx].PkhAdr)
		if err != nil {
			return err
		}
		// make new input from this utxo
		thisInput := wire.NewTxIn(&utxo.Op, prevPKscript)
		tx.AddTxIn(thisInput)
		nokori -= utxo.Value
		if nokori < -10000 { // minimum overage / fee is 1K now
			break
		}
	}
	// there's enough left to make a change output
	if nokori < -200000 {
		change, err := s.TS.NewAdr()
		if err != nil {
			return err
		}

		changeScript, err := txscript.PayToAddrScript(change)
		if err != nil {
			return err
		}
		changeOut := wire.NewTxOut((-100000)-nokori, changeScript)
		tx.AddTxOut(changeOut)
	}

	// use txstore method to sign
	err = s.TS.SignThis(tx)
	if err != nil {
		return err
	}

	fmt.Printf("tx: %s", uspv.TxToString(tx))
	buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
	tx.Serialize(buf)
	fmt.Printf("tx: %x\n", buf.Bytes())

	// 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
}
예제 #23
0
// createTx selects inputs (from the given slice of eligible utxos)
// whose amount are sufficient to fulfil all the desired outputs plus
// the mining fee. It then creates and returns a CreatedTx containing
// the selected inputs and the given outputs, validating it (using
// validateMsgTx) as well.
func createTx(eligible []wtxmgr.Credit,
	outputs map[string]btcutil.Amount, bs *waddrmgr.BlockStamp,
	feeIncrement btcutil.Amount, mgr *waddrmgr.Manager, account uint32,
	changeAddress func(account uint32) (btcutil.Address, error),
	chainParams *chaincfg.Params, disallowFree bool) (*CreatedTx, error) {

	msgtx := wire.NewMsgTx()
	minAmount, err := addOutputs(msgtx, outputs, chainParams)
	if err != nil {
		return nil, err
	}

	// Sort eligible inputs so that we first pick the ones with highest
	// amount, thus reducing number of inputs.
	sort.Sort(sort.Reverse(ByAmount(eligible)))

	// Start by adding enough inputs to cover for the total amount of all
	// desired outputs.
	var input wtxmgr.Credit
	var inputs []wtxmgr.Credit
	totalAdded := btcutil.Amount(0)
	for totalAdded < minAmount {
		if len(eligible) == 0 {
			return nil, InsufficientFundsError{totalAdded, minAmount, 0}
		}
		input, eligible = eligible[0], eligible[1:]
		inputs = append(inputs, input)
		msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
		totalAdded += input.Amount
	}

	// Get an initial fee estimate based on the number of selected inputs
	// and added outputs, with no change.
	szEst := estimateTxSize(len(inputs), len(msgtx.TxOut))
	feeEst := minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)

	// Now make sure the sum amount of all our inputs is enough for the
	// sum amount of all outputs plus the fee. If necessary we add more,
	// inputs, but in that case we also need to recalculate the fee.
	for totalAdded < minAmount+feeEst {
		if len(eligible) == 0 {
			return nil, InsufficientFundsError{totalAdded, minAmount, feeEst}
		}
		input, eligible = eligible[0], eligible[1:]
		inputs = append(inputs, input)
		msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
		szEst += txInEstimate
		totalAdded += input.Amount
		feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
	}

	var changeAddr btcutil.Address
	// changeIdx is -1 unless there's a change output.
	changeIdx := -1

	for {
		change := totalAdded - minAmount - feeEst
		if change > 0 {
			if changeAddr == nil {
				changeAddr, err = changeAddress(account)
				if err != nil {
					return nil, err
				}
			}

			changeIdx, err = addChange(msgtx, change, changeAddr)
			if err != nil {
				return nil, err
			}
		}

		if err = signMsgTx(msgtx, inputs, mgr, chainParams); err != nil {
			return nil, err
		}

		if feeForSize(feeIncrement, msgtx.SerializeSize()) <= feeEst {
			// The required fee for this size is less than or equal to what
			// we guessed, so we're done.
			break
		}

		if change > 0 {
			// Remove the change output since the next iteration will add
			// it again (with a new amount) if necessary.
			tmp := msgtx.TxOut[:changeIdx]
			tmp = append(tmp, msgtx.TxOut[changeIdx+1:]...)
			msgtx.TxOut = tmp
		}

		feeEst += feeIncrement
		for totalAdded < minAmount+feeEst {
			if len(eligible) == 0 {
				return nil, InsufficientFundsError{totalAdded, minAmount, feeEst}
			}
			input, eligible = eligible[0], eligible[1:]
			inputs = append(inputs, input)
			msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
			szEst += txInEstimate
			totalAdded += input.Amount
			feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
		}
	}

	if err := validateMsgTx(msgtx, inputs); err != nil {
		return nil, err
	}

	info := &CreatedTx{
		MsgTx:       msgtx,
		ChangeAddr:  changeAddr,
		ChangeIndex: changeIdx,
	}
	return info, nil
}
예제 #24
0
func Test_dupTx(t *testing.T) {

	// Ignore db remove errors since it means we didn't have an old one.
	dbname := fmt.Sprintf("tstdbdup0")
	dbnamever := dbname + ".ver"
	_ = os.RemoveAll(dbname)
	_ = os.RemoveAll(dbnamever)
	db, err := database.CreateDB("leveldb", dbname)
	if err != nil {
		t.Errorf("Failed to open test database %v", err)
		return
	}
	defer os.RemoveAll(dbname)
	defer os.RemoveAll(dbnamever)
	defer func() {
		if err := db.Close(); err != nil {
			t.Errorf("Close: unexpected error: %v", err)
		}
	}()

	testdatafile := filepath.Join("testdata", "blocks1-256.bz2")
	blocks, err := loadBlocks(t, testdatafile)
	if err != nil {
		t.Errorf("Unable to load blocks from test data for: %v",
			err)
		return
	}

	var lastSha *wire.ShaHash

	// Populate with the fisrt 256 blocks, so we have blocks to 'mess with'
	err = nil
out:
	for height := int64(0); height < int64(len(blocks)); height++ {
		block := blocks[height]

		// except for NoVerify which does not allow lookups check inputs
		mblock := block.MsgBlock()
		var txneededList []*wire.ShaHash
		for _, tx := range mblock.Transactions {
			for _, txin := range tx.TxIn {
				if txin.PreviousOutPoint.Index == uint32(4294967295) {
					continue
				}
				origintxsha := &txin.PreviousOutPoint.Hash
				txneededList = append(txneededList, origintxsha)

				exists, err := db.ExistsTxSha(origintxsha)
				if err != nil {
					t.Errorf("ExistsTxSha: unexpected error %v ", err)
				}
				if !exists {
					t.Errorf("referenced tx not found %v ", origintxsha)
				}

				_, err = db.FetchTxBySha(origintxsha)
				if err != nil {
					t.Errorf("referenced tx not found %v err %v ", origintxsha, err)
				}
			}
		}
		txlist := db.FetchUnSpentTxByShaList(txneededList)
		for _, txe := range txlist {
			if txe.Err != nil {
				t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
				break out
			}
		}

		newheight, err := db.InsertBlock(block)
		if err != nil {
			t.Errorf("failed to insert block %v err %v", height, err)
			break out
		}
		if newheight != height {
			t.Errorf("height mismatch expect %v returned %v", height, newheight)
			break out
		}

		newSha, blkid, err := db.NewestSha()
		if err != nil {
			t.Errorf("failed to obtain latest sha %v %v", height, err)
		}

		if blkid != height {
			t.Errorf("height doe not match latest block height %v %v %v", blkid, height, err)
		}

		blkSha := block.Sha()
		if *newSha != *blkSha {
			t.Errorf("Newest block sha does not match freshly inserted one %v %v %v ", newSha, blkSha, err)
		}
		lastSha = blkSha
	}

	// generate a new block based on the last sha
	// these block are not verified, so there are a bunch of garbage fields
	// in the 'generated' block.

	var bh wire.BlockHeader

	bh.Version = 2
	bh.PrevBlock = *lastSha
	// Bits, Nonce are not filled in

	mblk := wire.NewMsgBlock(&bh)

	hash, _ := wire.NewShaHashFromStr("df2b060fa2e5e9c8ed5eaf6a45c13753ec8c63282b2688322eba40cd98ea067a")

	po := wire.NewOutPoint(hash, 0)
	txI := wire.NewTxIn(po, []byte("garbage"))
	txO := wire.NewTxOut(50000000, []byte("garbageout"))

	var tx wire.MsgTx
	tx.AddTxIn(txI)
	tx.AddTxOut(txO)

	mblk.AddTransaction(&tx)

	blk := btcutil.NewBlock(mblk)

	fetchList := []*wire.ShaHash{hash}
	listReply := db.FetchUnSpentTxByShaList(fetchList)
	for _, lr := range listReply {
		if lr.Err != nil {
			t.Errorf("sha %v spent %v err %v\n", lr.Sha,
				lr.TxSpent, lr.Err)
		}
	}

	_, err = db.InsertBlock(blk)
	if err != nil {
		t.Errorf("failed to insert phony block %v", err)
	}

	// ok, did it 'spend' the tx ?

	listReply = db.FetchUnSpentTxByShaList(fetchList)
	for _, lr := range listReply {
		if lr.Err != database.ErrTxShaMissing {
			t.Errorf("sha %v spent %v err %v\n", lr.Sha,
				lr.TxSpent, lr.Err)
		}
	}

	txlist := blk.Transactions()
	for _, tx := range txlist {
		txsha := tx.Sha()
		txReply, err := db.FetchTxBySha(txsha)
		if err != nil {
			t.Errorf("fully spent lookup %v err %v\n", hash, err)
		} else {
			for _, lr := range txReply {
				if lr.Err != nil {
					t.Errorf("stx %v spent %v err %v\n", lr.Sha,
						lr.TxSpent, lr.Err)
				}
			}
		}
	}

	t.Logf("Dropping block")

	err = db.DropAfterBlockBySha(lastSha)
	if err != nil {
		t.Errorf("failed to drop spending block %v", err)
	}
}
예제 #25
0
// TestTx tests the MsgTx API.
func TestTx(t *testing.T) {
	pver := wire.ProtocolVersion

	// Block 100000 hash.
	hashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
	hash, err := wire.NewShaHashFromStr(hashStr)
	if err != nil {
		t.Errorf("NewShaHashFromStr: %v", err)
	}

	// Ensure the command is expected value.
	wantCmd := "tx"
	msg := wire.NewMsgTx()
	if cmd := msg.Command(); cmd != wantCmd {
		t.Errorf("NewMsgAddr: wrong command - got %v want %v",
			cmd, wantCmd)
	}

	// Ensure max payload is expected value for latest protocol version.
	// Num addresses (varInt) + max allowed addresses.
	wantPayload := uint32(1000 * 1000)
	maxPayload := msg.MaxPayloadLength(pver)
	if maxPayload != wantPayload {
		t.Errorf("MaxPayloadLength: wrong max payload length for "+
			"protocol version %d - got %v, want %v", pver,
			maxPayload, wantPayload)
	}

	// Ensure we get the same transaction output point data back out.
	// NOTE: This is a block hash and made up index, but we're only
	// testing package functionality.
	prevOutIndex := uint32(1)
	prevOut := wire.NewOutPoint(hash, prevOutIndex)
	if !prevOut.Hash.IsEqual(hash) {
		t.Errorf("NewOutPoint: wrong hash - got %v, want %v",
			spew.Sprint(&prevOut.Hash), spew.Sprint(hash))
	}
	if prevOut.Index != prevOutIndex {
		t.Errorf("NewOutPoint: wrong index - got %v, want %v",
			prevOut.Index, prevOutIndex)
	}
	prevOutStr := fmt.Sprintf("%s:%d", hash.String(), prevOutIndex)
	if s := prevOut.String(); s != prevOutStr {
		t.Errorf("OutPoint.String: unexpected result - got %v, "+
			"want %v", s, prevOutStr)
	}

	// Ensure we get the same transaction input back out.
	sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
	txIn := wire.NewTxIn(prevOut, sigScript)
	if !reflect.DeepEqual(&txIn.PreviousOutPoint, prevOut) {
		t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
			spew.Sprint(&txIn.PreviousOutPoint),
			spew.Sprint(prevOut))
	}
	if !bytes.Equal(txIn.SignatureScript, sigScript) {
		t.Errorf("NewTxIn: wrong signature script - got %v, want %v",
			spew.Sdump(txIn.SignatureScript),
			spew.Sdump(sigScript))
	}

	// Ensure we get the same transaction output back out.
	txValue := int64(5000000000)
	pkScript := []byte{
		0x41, // OP_DATA_65
		0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
		0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
		0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
		0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
		0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
		0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
		0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
		0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
		0xa6, // 65-byte signature
		0xac, // OP_CHECKSIG
	}
	txOut := wire.NewTxOut(txValue, pkScript)
	if txOut.Value != txValue {
		t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
			txOut.Value, txValue)

	}
	if !bytes.Equal(txOut.PkScript, pkScript) {
		t.Errorf("NewTxOut: wrong pk script - got %v, want %v",
			spew.Sdump(txOut.PkScript),
			spew.Sdump(pkScript))
	}

	// Ensure transaction inputs are added properly.
	msg.AddTxIn(txIn)
	if !reflect.DeepEqual(msg.TxIn[0], txIn) {
		t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v",
			spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn))
	}

	// Ensure transaction outputs are added properly.
	msg.AddTxOut(txOut)
	if !reflect.DeepEqual(msg.TxOut[0], txOut) {
		t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v",
			spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut))
	}

	// Ensure the copy produced an identical transaction message.
	newMsg := msg.Copy()
	if !reflect.DeepEqual(newMsg, msg) {
		t.Errorf("Copy: mismatched tx messages - got %v, want %v",
			spew.Sdump(newMsg), spew.Sdump(msg))
	}

	return
}
예제 #26
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)
			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)
			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
			}
		}
	}
}
예제 #27
0
func (w *LibbitcoinWallet) Spend(amount int64, addr btc.Address, feeLevel bitcoin.FeeLevel) error {
	// Check for dust
	script, _ := txscript.PayToAddrScript(addr)
	if txrules.IsDustAmount(btc.Amount(amount), len(script), txrules.DefaultRelayFeePerKb) {
		return errors.New("Amount is below dust threshold")
	}

	var additionalPrevScripts map[wire.OutPoint][]byte
	var additionalKeysByAddress map[string]*btc.WIF

	// Create input source
	coinMap := w.gatherCoins()
	coins := make([]coinset.Coin, 0, len(coinMap))
	for k := range coinMap {
		coins = append(coins, k)
	}
	inputSource := func(target btc.Amount) (total btc.Amount, inputs []*wire.TxIn, scripts [][]byte, err error) {
		// TODO: maybe change the coin selection algorithm? We're using min coins right now because
		// TODO: we don't know the number of confirmations on each coin without querying the libbitcoin server.
		coinSelector := coinset.MinNumberCoinSelector{MaxInputs: 10000, MinChangeAmount: btc.Amount(10000)}
		coins, err := coinSelector.CoinSelect(target, coins)
		if err != nil {
			return total, inputs, scripts, errors.New("insuffient funds")
		}
		additionalPrevScripts = make(map[wire.OutPoint][]byte)
		additionalKeysByAddress = make(map[string]*btc.WIF)
		for _, c := range coins.Coins() {
			total += c.Value()
			outpoint := wire.NewOutPoint(c.Hash(), c.Index())
			in := wire.NewTxIn(outpoint, []byte{})
			in.Sequence = 0 // Opt-in RBF so we can bump fees
			inputs = append(inputs, in)
			additionalPrevScripts[*outpoint] = c.PkScript()
			key := coinMap[c]
			addr, _ := btc.NewAddressPubKey(key.PublicKey().Key, w.params)
			pk, _ := btcec.PrivKeyFromBytes(btcec.S256(), key.Key)
			wif, _ := btc.NewWIF(pk, w.params, true)
			additionalKeysByAddress[addr.AddressPubKeyHash().EncodeAddress()] = wif
		}
		return total, inputs, scripts, nil
	}

	// Get the fee per kilobyte
	feePerKB := int64(w.getFeePerByte(feeLevel)) * 1000

	// outputs
	out := wire.NewTxOut(amount, script)

	// Create change source
	changeSource := func() ([]byte, error) {
		addr := w.GetCurrentAddress(bitcoin.CHANGE)
		script, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return []byte{}, err
		}
		return script, nil
	}

	authoredTx, err := txauthor.NewUnsignedTransaction([]*wire.TxOut{out}, btc.Amount(feePerKB), inputSource, changeSource)
	if err != nil {
		return err
	}

	// BIP 69 sorting
	txsort.InPlaceSort(authoredTx.Tx)

	// Sign tx
	getKey := txscript.KeyClosure(func(addr btc.Address) (
		*btcec.PrivateKey, bool, error) {
		addrStr := addr.EncodeAddress()
		wif := additionalKeysByAddress[addrStr]
		return wif.PrivKey, wif.CompressPubKey, nil
	})
	getScript := txscript.ScriptClosure(func(
		addr btc.Address) ([]byte, error) {
		return []byte{}, nil
	})
	for i, txIn := range authoredTx.Tx.TxIn {
		prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint]
		script, err := txscript.SignTxOutput(w.params,
			authoredTx.Tx, i, prevOutScript, txscript.SigHashAll, getKey,
			getScript, txIn.SignatureScript)
		if err != nil {
			return errors.New("Failed to sign transaction")
		}
		txIn.SignatureScript = script
	}

	// Broadcast tx to bitcoin network
	serializedTx := new(bytes.Buffer)
	authoredTx.Tx.Serialize(serializedTx)
	w.Client.Broadcast(serializedTx.Bytes(), func(i interface{}, err error) {
		if err == nil {
			log.Infof("Broadcast tx %s to bitcoin network\n", authoredTx.Tx.TxSha().String())
		} else {
			log.Errorf("Failed to broadcast tx, reason: %s\n", err)
		}
	})

	// Update the db
	w.ProcessTransaction(btc.NewTx(authoredTx.Tx), 0)

	return nil
}