github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/signtx.go (about) 1 package qln 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/mit-dci/lit/btcutil/txscript" 8 "github.com/mit-dci/lit/crypto/koblitz" 9 "github.com/mit-dci/lit/lnutil" 10 "github.com/mit-dci/lit/logging" 11 "github.com/mit-dci/lit/sig64" 12 "github.com/mit-dci/lit/wire" 13 ) 14 15 // SignBreak signs YOUR tx, which you already have a sig for 16 func (nd *LitNode) SignBreakTx(q *Qchan) (*wire.MsgTx, error) { 17 // TODO: we probably have to do something with the HTLCs here 18 19 tx, _, _, err := q.BuildStateTxs(true) 20 if err != nil { 21 return nil, err 22 } 23 24 // make hash cache for this tx 25 hCache := txscript.NewTxSigHashes(tx) 26 27 // generate script preimage (keep track of key order) 28 pre, swap, err := lnutil.FundTxScript(q.MyPub, q.TheirPub) 29 if err != nil { 30 return nil, err 31 } 32 33 // get private signing key 34 priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen) 35 if err != nil { 36 return nil, err 37 } 38 // generate sig. 39 mySig, err := txscript.RawTxInWitnessSignature( 40 tx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv) 41 if err != nil { 42 return nil, err 43 } 44 45 theirSig := sig64.SigDecompress(q.State.Sig) 46 // put the sighash all byte on the end of their signature 47 theirSig = append(theirSig, byte(txscript.SigHashAll)) 48 49 logging.Infof("made mysig: %x theirsig: %x\n", mySig, theirSig) 50 // add sigs to the witness stack 51 if swap { 52 tx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirSig, mySig) 53 } else { 54 tx.TxIn[0].Witness = SpendMultiSigWitStack(pre, mySig, theirSig) 55 } 56 57 // save channel state as closed 58 // Removed - this is already done in the calling function - and is killing 59 // the ability to just print the TX. 60 // q.CloseData.Closed = true 61 // q.CloseData.CloseTxid = tx.TxHash() 62 // err = nd.SaveQchanUtxoData(q) 63 // if err != nil { 64 // return nil, err 65 // } 66 67 return tx, nil 68 } 69 70 // SignSimpleClose signs the given simpleClose tx, given the other signature 71 // Tx is modified in place. 72 func (nd *LitNode) SignSimpleClose(q *Qchan, tx *wire.MsgTx) ([64]byte, error) { 73 74 var sig [64]byte 75 // make hash cache 76 hCache := txscript.NewTxSigHashes(tx) 77 78 // generate script preimage for signing (ignore key order) 79 pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub) 80 if err != nil { 81 return sig, err 82 } 83 // get private signing key 84 priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen) 85 if err != nil { 86 return sig, err 87 } 88 // generate sig 89 mySig, err := txscript.RawTxInWitnessSignature( 90 tx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv) 91 if err != nil { 92 return sig, err 93 } 94 // truncate sig (last byte is sighash type, always sighashAll) 95 mySig = mySig[:len(mySig)-1] 96 return sig64.SigCompress(mySig) 97 } 98 99 // SignSettlementTx signs the given settlement tx based on the passed contract 100 // using the passed private key. Tx is modified in place. 101 func (nd *LitNode) SignSettlementTx(c *lnutil.DlcContract, tx *wire.MsgTx, 102 priv *koblitz.PrivateKey) ([64]byte, error) { 103 104 var sig [64]byte 105 // make hash cache 106 hCache := txscript.NewTxSigHashes(tx) 107 108 // generate script preimage for signing (ignore key order) 109 pre, _, err := lnutil.FundTxScript(c.OurFundMultisigPub, 110 c.TheirFundMultisigPub) 111 112 if err != nil { 113 return sig, err 114 } 115 // generate sig 116 mySig, err := txscript.RawTxInWitnessSignature( 117 tx, hCache, 0, c.TheirFundingAmount+c.OurFundingAmount, 118 pre, txscript.SigHashAll, priv) 119 120 if err != nil { 121 return sig, err 122 } 123 // truncate sig (last byte is sighash type, always sighashAll) 124 mySig = mySig[:len(mySig)-1] 125 return sig64.SigCompress(mySig) 126 } 127 128 // SignClaimTx signs the given claim tx based on the passed preimage and value 129 // using the passed private key. Tx is modified in place. timeout=false means 130 // it's a regular claim, timeout=true means we're claiming an output that has 131 // expired (for instance if someone) published the wrong settlement TX, we can 132 // claim this output back to our wallet after the timelock expired. 133 func (nd *LitNode) SignClaimTx(claimTx *wire.MsgTx, value int64, pre []byte, 134 priv *koblitz.PrivateKey, timeout bool) error { 135 136 // make hash cache 137 hCache := txscript.NewTxSigHashes(claimTx) 138 139 // generate sig 140 mySig, err := txscript.RawTxInWitnessSignature( 141 claimTx, hCache, 0, value, pre, txscript.SigHashAll, priv) 142 if err != nil { 143 return err 144 } 145 146 witStash := make([][]byte, 3) 147 witStash[0] = mySig 148 if timeout { 149 witStash[1] = nil 150 } else { 151 witStash[1] = []byte{0x01} 152 } 153 witStash[2] = pre 154 claimTx.TxIn[0].Witness = witStash 155 return nil 156 } 157 158 // SignNextState generates your signature for their state. 159 func (nd *LitNode) SignState(q *Qchan) ([64]byte, [][64]byte, error) { 160 var sig [64]byte 161 162 // make sure channel exists, and wallet is present on node 163 if q == nil { 164 return sig, nil, fmt.Errorf("SignState nil channel") 165 } 166 _, ok := nd.SubWallet[q.Coin()] 167 if !ok { 168 return sig, nil, fmt.Errorf("SignState no wallet for cointype %d", q.Coin()) 169 } 170 // build transaction for next state 171 commitmentTx, spendHTLCTxs, HTLCTxOuts, err := q.BuildStateTxs(false) // their tx, as I'm signing 172 if err != nil { 173 return sig, nil, err 174 } 175 176 logging.Infof("Signing state with Elk [%x] NextElk [%x] N2Elk [%x]\n", q.State.ElkPoint, q.State.NextElkPoint, q.State.N2ElkPoint) 177 178 // make hash cache for this tx 179 hCache := txscript.NewTxSigHashes(commitmentTx) 180 181 // generate script preimage (ignore key order) 182 pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub) 183 if err != nil { 184 return sig, nil, err 185 } 186 187 // get private signing key 188 priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen) 189 if err != nil { 190 return sig, nil, err 191 } 192 193 // generate sig. 194 bigSig, err := txscript.RawTxInWitnessSignature( 195 commitmentTx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv) 196 if err != nil { 197 return sig, nil, err 198 } 199 200 // truncate sig (last byte is sighash type, always sighashAll) 201 bigSig = bigSig[:len(bigSig)-1] 202 203 sig, err = sig64.SigCompress(bigSig) 204 if err != nil { 205 return sig, nil, err 206 } 207 208 logging.Infof("____ sig creation for channel (%d,%d):\n", q.Peer(), q.Idx()) 209 logging.Infof("\tinput %s\n", commitmentTx.TxIn[0].PreviousOutPoint.String()) 210 for i, txout := range commitmentTx.TxOut { 211 logging.Infof("\toutput %d: %x %d\n", i, txout.PkScript, txout.Value) 212 } 213 214 logging.Infof("\tstate %d myamt: %d theiramt: %d\n", q.State.StateIdx, q.State.MyAmt, q.Value-q.State.MyAmt) 215 216 // Generate signatures for HTLC-success/failure transactions 217 spendHTLCSigs := map[int][64]byte{} 218 219 curElk, err := q.ElkSnd.AtIndex(q.State.StateIdx) 220 if err != nil { 221 return sig, nil, err 222 } 223 elkScalar := lnutil.ElkScalar(curElk) 224 225 ep := lnutil.ElkPointFromHash(curElk) 226 227 logging.Infof("Using elkpoint %x to sign HTLC txs", ep) 228 229 for idx, h := range HTLCTxOuts { 230 // Find out which vout this HTLC is in the commitment tx since BIP69 231 // potentially reordered them 232 var where uint32 233 for i, o := range commitmentTx.TxOut { 234 if bytes.Compare(o.PkScript, h.PkScript) == 0 { 235 where = uint32(i) 236 break 237 } 238 } 239 240 var HTLCPrivBase *koblitz.PrivateKey 241 if idx == len(q.State.HTLCs) { 242 HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.InProgHTLC.KeyGen) 243 } else if idx == len(q.State.HTLCs)+1 { 244 HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.CollidingHTLC.KeyGen) 245 } else { 246 HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.HTLCs[idx].KeyGen) 247 } 248 249 if err != nil { 250 return sig, nil, err 251 } 252 253 HTLCPriv := lnutil.CombinePrivKeyWithBytes(HTLCPrivBase, elkScalar[:]) 254 255 // Find the tx we need to sign. (this would all be much easier if we 256 // didn't use BIP69) 257 var spendTx *wire.MsgTx 258 var which int 259 for i, t := range spendHTLCTxs { 260 if t.TxIn[0].PreviousOutPoint.Index == where { 261 spendTx = t 262 which = i 263 break 264 } 265 } 266 267 hc := txscript.NewTxSigHashes(spendTx) 268 var HTLCScript []byte 269 270 if idx == len(q.State.HTLCs) { 271 HTLCScript, err = q.GenHTLCScript(*q.State.InProgHTLC, false) 272 } else if idx == len(q.State.HTLCs)+1 { 273 HTLCScript, err = q.GenHTLCScript(*q.State.CollidingHTLC, false) 274 } else { 275 HTLCScript, err = q.GenHTLCScript(q.State.HTLCs[idx], false) 276 } 277 if err != nil { 278 return sig, nil, err 279 } 280 281 HTLCparsed, err := txscript.ParseScript(HTLCScript) 282 if err != nil { 283 return sig, nil, err 284 } 285 286 spendHTLCHash := txscript.CalcWitnessSignatureHash( 287 HTLCparsed, hc, txscript.SigHashAll, spendTx, 0, h.Value) 288 289 logging.Infof("Signing HTLC hash: %x, with pubkey: %x", spendHTLCHash, HTLCPriv.PubKey().SerializeCompressed()) 290 291 mySig, err := HTLCPriv.Sign(spendHTLCHash) 292 if err != nil { 293 return sig, nil, err 294 } 295 296 HTLCSig := mySig.Serialize() 297 s, err := sig64.SigCompress(HTLCSig) 298 if err != nil { 299 return sig, nil, err 300 } 301 302 spendHTLCSigs[which] = s 303 } 304 305 // Get the sigs in the same order as the HTLCs in the tx 306 var spendHTLCSigsArr [][64]byte 307 for i := 0; i < len(spendHTLCSigs)+2; i++ { 308 if s, ok := spendHTLCSigs[i]; ok { 309 spendHTLCSigsArr = append(spendHTLCSigsArr, s) 310 } 311 } 312 313 return sig, spendHTLCSigsArr, err 314 } 315 316 // VerifySig verifies their signature for your next state. 317 // it also saves the sig if it's good. 318 // do bool, error or just error? Bad sig is an error I guess. 319 // for verifying signature, always use theirHAKDpub, so generate & populate within 320 // this function. 321 func (q *Qchan) VerifySigs(sig [64]byte, HTLCSigs [][64]byte) error { 322 323 bigSig := sig64.SigDecompress(sig) 324 // my tx when I'm verifying. 325 commitmentTx, spendHTLCTxs, HTLCTxOuts, err := q.BuildStateTxs(true) 326 if err != nil { 327 return err 328 } 329 330 logging.Infof("Verifying signatures with Elk [%x] NextElk [%x] N2Elk [%x]\n", q.State.ElkPoint, q.State.NextElkPoint, q.State.N2ElkPoint) 331 332 // generate fund output script preimage (ignore key order) 333 pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub) 334 if err != nil { 335 return err 336 } 337 338 hCache := txscript.NewTxSigHashes(commitmentTx) 339 340 parsed, err := txscript.ParseScript(pre) 341 if err != nil { 342 return err 343 } 344 // always sighash all 345 hash := txscript.CalcWitnessSignatureHash( 346 parsed, hCache, txscript.SigHashAll, commitmentTx, 0, q.Value) 347 348 // sig is pre-truncated; last byte for sighashtype is always sighashAll 349 pSig, err := koblitz.ParseDERSignature(bigSig, koblitz.S256()) 350 if err != nil { 351 return err 352 } 353 theirPubKey, err := koblitz.ParsePubKey(q.TheirPub[:], koblitz.S256()) 354 if err != nil { 355 return err 356 } 357 logging.Infof("____ sig verification for channel (%d,%d):\n", q.Peer(), q.Idx()) 358 logging.Infof("\tinput %s\n", commitmentTx.TxIn[0].PreviousOutPoint.String()) 359 for i, txout := range commitmentTx.TxOut { 360 logging.Infof("\toutput %d: %x %d\n", i, txout.PkScript, txout.Value) 361 } 362 logging.Infof("\tstate %d myamt: %d theiramt: %d\n", q.State.StateIdx, q.State.MyAmt, q.Value-q.State.MyAmt) 363 logging.Infof("\tsig: %x\n", sig) 364 365 worked := pSig.Verify(hash, theirPubKey) 366 if !worked { 367 return fmt.Errorf("Invalid signature on chan %d state %d", 368 q.Idx(), q.State.StateIdx) 369 } 370 371 // Verify HTLC-success/failure signatures 372 373 if len(HTLCSigs) != len(spendHTLCTxs) { 374 return fmt.Errorf("Wrong number of signatures provided for HTLCs in channel. Got %d expected %d.", 375 len(HTLCSigs), len(spendHTLCTxs)) 376 } 377 378 // Map HTLC index to signature index 379 sigIndex := map[uint32]uint32{} 380 381 logging.Infof("Using elkpoint %x to verify HTLC txs", q.State.NextElkPoint) 382 383 for idx, h := range HTLCTxOuts { 384 // Find out which vout this HTLC is in the commitment tx since BIP69 385 // potentially reordered them 386 var where uint32 387 for i, o := range commitmentTx.TxOut { 388 if bytes.Compare(o.PkScript, h.PkScript) == 0 { 389 where = uint32(i) 390 break 391 } 392 } 393 394 // Find the tx we need to verify. (this would all be much easier if we 395 // didn't use BIP69) 396 var spendTx *wire.MsgTx 397 var which int 398 for i, t := range spendHTLCTxs { 399 if t.TxIn[0].PreviousOutPoint.Index == where { 400 spendTx = t 401 which = i 402 sigIndex[uint32(idx)] = uint32(which) 403 break 404 } 405 } 406 407 hc := txscript.NewTxSigHashes(spendTx) 408 var HTLCScript []byte 409 if idx == len(q.State.HTLCs) { 410 HTLCScript, err = q.GenHTLCScript(*q.State.InProgHTLC, true) 411 } else if idx == len(q.State.HTLCs)+1 { 412 HTLCScript, err = q.GenHTLCScript(*q.State.CollidingHTLC, true) 413 } else { 414 HTLCScript, err = q.GenHTLCScript(q.State.HTLCs[idx], true) 415 } 416 if err != nil { 417 return err 418 } 419 420 HTLCparsed, err := txscript.ParseScript(HTLCScript) 421 if err != nil { 422 return err 423 } 424 // always sighash all 425 spendHTLCHash := txscript.CalcWitnessSignatureHash( 426 HTLCparsed, hc, txscript.SigHashAll, spendTx, 0, h.Value) 427 428 // sig is pre-truncated; last byte for sighashtype is always sighashAll 429 HTLCSig, err := koblitz.ParseDERSignature(sig64.SigDecompress(HTLCSigs[which]), koblitz.S256()) 430 if err != nil { 431 return err 432 } 433 434 var theirHTLCPub [33]byte 435 if idx == len(q.State.HTLCs) { 436 theirHTLCPub = lnutil.CombinePubs(q.State.InProgHTLC.TheirHTLCBase, q.State.NextElkPoint) 437 } else if idx == len(q.State.HTLCs)+1 { 438 theirHTLCPub = lnutil.CombinePubs(q.State.CollidingHTLC.TheirHTLCBase, q.State.NextElkPoint) 439 } else { 440 theirHTLCPub = lnutil.CombinePubs(q.State.HTLCs[idx].TheirHTLCBase, q.State.NextElkPoint) 441 } 442 443 theirHTLCPubKey, err := koblitz.ParsePubKey(theirHTLCPub[:], koblitz.S256()) 444 if err != nil { 445 return err 446 } 447 448 logging.Infof("Verifying HTLC hash: %x, with pubkey: %x", spendHTLCHash, theirHTLCPub) 449 450 sigValid := HTLCSig.Verify(spendHTLCHash, theirHTLCPubKey) 451 if !sigValid { 452 return fmt.Errorf("Invalid signature HTLC on chan %d state %d HTLC %d", 453 q.Idx(), q.State.StateIdx, idx) 454 } 455 } 456 457 // copy signature, overwriting old signature. 458 q.State.Sig = sig 459 460 // copy HTLC-success/failure signatures 461 for i, s := range sigIndex { 462 if int(i) == len(q.State.HTLCs) { 463 q.State.InProgHTLC.Sig = HTLCSigs[s] 464 } else if int(i) == len(q.State.HTLCs)+1 { 465 q.State.CollidingHTLC.Sig = HTLCSigs[s] 466 } else { 467 q.State.HTLCs[i].Sig = HTLCSigs[s] 468 } 469 } 470 471 return nil 472 }