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 }