github.com/decred/dcrlnd@v0.7.6/lnwallet/remotedcrwallet/signer.go (about)

     1  package remotedcrwallet
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"google.golang.org/grpc/codes"
    10  	"google.golang.org/grpc/status"
    11  
    12  	pb "decred.org/dcrwallet/v4/rpc/walletrpc"
    13  	"github.com/decred/dcrd/chaincfg/chainhash"
    14  	"github.com/decred/dcrd/dcrec"
    15  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    16  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    17  	"github.com/decred/dcrd/dcrutil/v4"
    18  	"github.com/decred/dcrd/txscript/v4"
    19  	"github.com/decred/dcrd/txscript/v4/sign"
    20  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    21  	"github.com/decred/dcrd/txscript/v4/stdscript"
    22  	"github.com/decred/dcrd/wire"
    23  	"github.com/decred/dcrlnd/btcwalletcompat"
    24  	"github.com/decred/dcrlnd/input"
    25  	"github.com/decred/dcrlnd/internal/psbt"
    26  	"github.com/decred/dcrlnd/keychain"
    27  	"github.com/decred/dcrlnd/lnwallet"
    28  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    29  )
    30  
    31  // FetchInputInfo queries for the WalletController's knowledge of the passed
    32  // outpoint. If the base wallet determines this output is under its control,
    33  // then the original txout should be returned. Otherwise, a non-nil error value
    34  // of ErrNotMine should be returned instead.
    35  //
    36  // This is a part of the WalletController interface.
    37  func (b *DcrWallet) FetchInputInfo(prevOut *wire.OutPoint) (*lnwallet.Utxo, error) {
    38  	// We manually look up the output within the tx store.
    39  	req := &pb.GetTransactionRequest{
    40  		TransactionHash: prevOut.Hash[:],
    41  	}
    42  	resp, err := b.wallet.GetTransaction(b.ctx, req)
    43  	if err != nil {
    44  		if status.Code(err) == codes.NotFound {
    45  			return nil, lnwallet.ErrNotMine
    46  		}
    47  		return nil, err
    48  	}
    49  
    50  	// With the output retrieved, we'll make an additional check to ensure
    51  	// we actually have control of this output. We do this because the
    52  	// check above only guarantees that the transaction is somehow relevant
    53  	// to us, like in the event of us being the sender of the transaction.
    54  	//
    55  	// Furthermore, we'll only consider our control outputs which were sent
    56  	// to the wallet account that dcrlnd controls.
    57  	var credit *pb.TransactionDetails_Output
    58  	for _, out := range resp.Transaction.Credits {
    59  		if out.Index == prevOut.Index && out.Account == b.account {
    60  			credit = out
    61  			break
    62  		}
    63  	}
    64  	if credit == nil {
    65  		return nil, lnwallet.ErrNotMine
    66  	}
    67  
    68  	// TODO(decred) The credit structure should return the script version.
    69  	scriptVersion := uint16(0)
    70  
    71  	// Then, we'll populate all of the information required by the struct.
    72  	addressType := lnwallet.UnknownAddressType
    73  	scriptClass := stdscript.DetermineScriptType(scriptVersion, credit.OutputScript)
    74  	switch scriptClass {
    75  	case stdscript.STPubKeyHashEcdsaSecp256k1:
    76  		addressType = lnwallet.PubKeyHash
    77  	}
    78  
    79  	prevTx := wire.NewMsgTx()
    80  	err = prevTx.Deserialize(bytes.NewBuffer(resp.Transaction.Transaction))
    81  	if err != nil {
    82  		return nil, fmt.Errorf("unable to decode raw tx: %v", err)
    83  	}
    84  
    85  	return &lnwallet.Utxo{
    86  		AddressType: addressType,
    87  		Value: dcrutil.Amount(
    88  			credit.Amount,
    89  		),
    90  		PkScript:      credit.OutputScript,
    91  		Confirmations: int64(resp.Confirmations),
    92  		OutPoint:      *prevOut,
    93  		PrevTx:        prevTx,
    94  	}, nil
    95  }
    96  
    97  // maybeTweakPrivKey examines the single and double tweak parameters on the
    98  // passed sign descriptor and may perform a mapping on the passed private key
    99  // in order to utilize the tweaks, if populated.
   100  func maybeTweakPrivKey(signDesc *input.SignDescriptor,
   101  	privKey *secp256k1.PrivateKey) (*secp256k1.PrivateKey, error) {
   102  
   103  	var retPriv *secp256k1.PrivateKey
   104  	switch {
   105  
   106  	case signDesc.SingleTweak != nil:
   107  		retPriv = input.TweakPrivKey(privKey,
   108  			signDesc.SingleTweak)
   109  
   110  	case signDesc.DoubleTweak != nil:
   111  		retPriv = input.DeriveRevocationPrivKey(privKey,
   112  			signDesc.DoubleTweak)
   113  
   114  	default:
   115  		retPriv = privKey
   116  	}
   117  
   118  	return retPriv, nil
   119  }
   120  
   121  // SignOutputRaw generates a signature for the passed transaction according to
   122  // the data within the passed input.SignDescriptor.
   123  //
   124  // This is a part of the WalletController interface.
   125  func (b *DcrWallet) SignOutputRaw(tx *wire.MsgTx,
   126  	signDesc *input.SignDescriptor) (input.Signature, error) {
   127  
   128  	witnessScript := signDesc.WitnessScript
   129  
   130  	// First attempt to fetch the private key which corresponds to the
   131  	// specified public key.
   132  	privKey, err := b.DerivePrivKey(signDesc.KeyDesc)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	// If a tweak (single or double) is specified, then we'll need to use
   138  	// this tweak to derive the final private key to be used for signing
   139  	// this output.
   140  	privKey, err = maybeTweakPrivKey(signDesc, privKey)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	// TODO(roasbeef): generate sighash midstate if not present?
   146  	sig, err := sign.RawTxInSignature(
   147  		tx, signDesc.InputIndex,
   148  		witnessScript, signDesc.HashType, privKey.Serialize(),
   149  		dcrec.STEcdsaSecp256k1,
   150  	)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	// Chop off the sighash flag at the end of the signature.
   156  	return ecdsa.ParseDERSignature(sig[:len(sig)-1])
   157  }
   158  
   159  // p2pkhSigScriptToWitness converts a raw sigScript that signs a p2pkh output
   160  // into a list of individual data pushes, amenable to be used a pseudo-witness
   161  // stack.
   162  func p2pkhSigScriptToWitness(scriptVersion uint16, sigScript []byte) ([][]byte, error) {
   163  	data := make([][]byte, 0, 2)
   164  	tokenizer := txscript.MakeScriptTokenizer(scriptVersion, sigScript)
   165  	for tokenizer.Next() && len(data) < 2 {
   166  		if tokenizer.Data() == nil {
   167  			return nil, errors.New("expected pushed data in p2pkh " +
   168  				"sigScript but found none")
   169  		}
   170  		data = append(data, tokenizer.Data())
   171  	}
   172  	if err := tokenizer.Err(); err != nil {
   173  		return nil, err
   174  
   175  	}
   176  	if len(data) != 2 {
   177  		return nil, fmt.Errorf("expected p2pkh sigScript to have 2 "+
   178  			"data pushes but found %d", len(data))
   179  	}
   180  	return data, nil
   181  
   182  }
   183  
   184  // ComputeInputScript generates a complete InputScript for the passed
   185  // transaction with the signature as defined within the passed input.SignDescriptor.
   186  // This method is capable of generating the proper input script only for
   187  // regular p2pkh outputs.
   188  //
   189  // This is a part of the WalletController interface.
   190  func (b *DcrWallet) ComputeInputScript(tx *wire.MsgTx,
   191  	signDesc *input.SignDescriptor) (*input.Script, error) {
   192  
   193  	script := signDesc.Output.PkScript
   194  	scriptVersion := signDesc.Output.Version
   195  
   196  	// Figure out the branch and index of the key needed to sign this
   197  	// input. This assumes only regular onchain keys are used with
   198  	// ComputeInputScript.
   199  	_, addrs := stdscript.ExtractAddrs(scriptVersion, script,
   200  		b.chainParams)
   201  	if len(addrs) != 1 {
   202  		return nil, fmt.Errorf("wrong number of addresses decoded")
   203  	}
   204  
   205  	validAddrReq := &pb.ValidateAddressRequest{
   206  		Address: addrs[0].String(),
   207  	}
   208  	validAddrResp, err := b.wallet.ValidateAddress(context.Background(), validAddrReq)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	if !validAddrResp.IsValid {
   213  		return nil, fmt.Errorf("invalid decoded address")
   214  	}
   215  	if !validAddrResp.IsMine {
   216  		return nil, fmt.Errorf("address is not mine")
   217  	}
   218  	if validAddrResp.AccountNumber != b.account {
   219  		return nil, fmt.Errorf("address is for account not controlled by dcrlnd")
   220  	}
   221  
   222  	// Fetch the private key for the given wallet address.
   223  	branchXPriv := b.branchExtXPriv
   224  	if validAddrResp.IsInternal {
   225  		branchXPriv = b.branchIntXPriv
   226  	}
   227  	extPrivKey, err := branchXPriv.Child(validAddrResp.Index)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	serPrivKey, err := extPrivKey.SerializedPrivKey()
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	privKey := secp256k1.PrivKeyFromBytes(serPrivKey)
   236  
   237  	// If a tweak (single or double) is specified, then we'll need to use
   238  	// this tweak to derive the final private key to be used for signing
   239  	// this output.
   240  	privKey, err = maybeTweakPrivKey(signDesc, privKey)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	// Generate a valid witness stack for the input.
   246  	// TODO(roasbeef): adhere to passed HashType
   247  	sigScript, err := sign.SignatureScript(tx, signDesc.InputIndex,
   248  		script, signDesc.HashType, privKey.Serialize(),
   249  		dcrec.STEcdsaSecp256k1, true)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	witness, err := p2pkhSigScriptToWitness(scriptVersion, sigScript)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	return &input.Script{Witness: witness}, nil
   260  }
   261  
   262  // A compile time check to ensure that DcrWallet implements the input.Signer
   263  // interface.
   264  var _ input.Signer = (*DcrWallet)(nil)
   265  
   266  // SignMessage attempts to sign a target message with the private key that
   267  // corresponds to the passed public key. If the target private key is unable to
   268  // be found, then an error will be returned. The actual digest signed is the
   269  // chainhash (blake256r14) of the passed message.
   270  //
   271  // NOTE: This is a part of the Messageinput.Signer interface.
   272  func (b *DcrWallet) SignMessage(keyLoc keychain.KeyLocator,
   273  	msg []byte, double bool) (*ecdsa.Signature, error) {
   274  
   275  	// First attempt to fetch the private key which corresponds to the
   276  	// specified public key.
   277  	privKey, err := b.DerivePrivKey(keychain.KeyDescriptor{KeyLocator: keyLoc})
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	// Double hash and sign the data.
   283  	var digest []byte
   284  	if double {
   285  		return nil, fmt.Errorf("dcrlnd does not do doubleHash signing")
   286  	} else {
   287  		digest = chainhash.HashB(msg)
   288  	}
   289  	sign := ecdsa.Sign(privKey, digest)
   290  
   291  	return sign, nil
   292  }
   293  
   294  // FundPsbt currently does nothing.
   295  func (b *DcrWallet) FundPsbt(_ *psbt.Packet, _ int32,
   296  	_ chainfee.AtomPerKByte, _ string) (int32, error) {
   297  
   298  	return 0, fmt.Errorf("FundPSBT not supported")
   299  }
   300  
   301  // FinalizePsbt currently does nothing.
   302  func (b *DcrWallet) FinalizePsbt(_ *psbt.Packet) error {
   303  	return fmt.Errorf("FinalizePsbt not supported")
   304  }
   305  
   306  // SignPsbt does nothing.
   307  func (b *DcrWallet) SignPsbt(*psbt.Packet) error {
   308  	return fmt.Errorf("SignPsbt not supported")
   309  }
   310  
   311  // AddressInfo returns info about a wallet address.
   312  func (b *DcrWallet) AddressInfo(
   313  	stdaddr.Address) (btcwalletcompat.ManagedAddress, error) {
   314  
   315  	return nil, fmt.Errorf("AddressInfo not supported")
   316  }