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