github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/dlc.go (about) 1 package qln 2 3 import ( 4 "fmt" 5 6 "github.com/mit-dci/lit/btcutil" 7 "github.com/mit-dci/lit/btcutil/txscript" 8 "github.com/mit-dci/lit/btcutil/txsort" 9 "github.com/mit-dci/lit/crypto/koblitz" 10 "github.com/mit-dci/lit/dlc" 11 "github.com/mit-dci/lit/lnutil" 12 "github.com/mit-dci/lit/logging" 13 "github.com/mit-dci/lit/portxo" 14 "github.com/mit-dci/lit/sig64" 15 "github.com/mit-dci/lit/wire" 16 ) 17 18 func (nd *LitNode) AddContract() (*lnutil.DlcContract, error) { 19 20 c, err := nd.DlcManager.AddContract() 21 if err != nil { 22 return nil, err 23 } 24 25 return c, nil 26 } 27 28 func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { 29 c, err := nd.DlcManager.LoadContract(cIdx) 30 if err != nil { 31 return err 32 } 33 34 if c.Status != lnutil.ContractStatusDraft { 35 return fmt.Errorf("You cannot offer a contract to someone that is not in draft stage") 36 } 37 38 if !nd.ConnectedToPeer(peerIdx) { 39 return fmt.Errorf("You are not connected to peer %d, do that first", peerIdx) 40 } 41 42 var nullBytes [33]byte 43 // Check if everything's set 44 if c.OracleA == nullBytes { 45 return fmt.Errorf("You need to set an oracle for the contract before offering it") 46 } 47 48 if c.OracleR == nullBytes { 49 return fmt.Errorf("You need to set an R-point for the contract before offering it") 50 } 51 52 if c.OracleTimestamp == 0 { 53 return fmt.Errorf("You need to set a settlement time for the contract before offering it") 54 } 55 56 if c.CoinType == dlc.COINTYPE_NOT_SET { 57 return fmt.Errorf("You need to set a coin type for the contract before offering it") 58 } 59 60 if c.Division == nil { 61 return fmt.Errorf("You need to set a payout division for the contract before offering it") 62 } 63 64 if c.OurFundingAmount+c.TheirFundingAmount == 0 { 65 return fmt.Errorf("You need to set a funding amount for the peers in contract before offering it") 66 } 67 68 c.PeerIdx = peerIdx 69 70 var kg portxo.KeyGen 71 kg.Depth = 5 72 kg.Step[0] = 44 | 1<<31 73 kg.Step[1] = c.CoinType | 1<<31 74 kg.Step[2] = UseContractFundMultisig 75 kg.Step[3] = c.PeerIdx | 1<<31 76 kg.Step[4] = uint32(c.Idx) | 1<<31 77 78 c.OurFundMultisigPub, err = nd.GetUsePub(kg, UseContractFundMultisig) 79 if err != nil { 80 return err 81 } 82 83 c.OurPayoutBase, err = nd.GetUsePub(kg, UseContractPayoutBase) 84 if err != nil { 85 return err 86 } 87 88 // Fund the contract 89 err = nd.FundContract(c) 90 if err != nil { 91 return err 92 } 93 94 msg := lnutil.NewDlcOfferMsg(peerIdx, c) 95 96 c.Status = lnutil.ContractStatusOfferedByMe 97 err = nd.DlcManager.SaveContract(c) 98 if err != nil { 99 return err 100 } 101 102 nd.tmpSendLitMsg(msg) 103 104 return nil 105 } 106 107 func (nd *LitNode) DeclineDlc(cIdx uint64, reason uint8) error { 108 c, err := nd.DlcManager.LoadContract(cIdx) 109 if err != nil { 110 return err 111 } 112 113 if c.Status != lnutil.ContractStatusOfferedToMe { 114 return fmt.Errorf("You cannot decline a contract unless it is in the 'Offered/Awaiting reply' state") 115 } 116 117 if !nd.ConnectedToPeer(c.PeerIdx) { 118 return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx) 119 } 120 121 msg := lnutil.NewDlcOfferDeclineMsg(c.PeerIdx, reason, c.TheirIdx) 122 c.Status = lnutil.ContractStatusDeclined 123 124 err = nd.DlcManager.SaveContract(c) 125 if err != nil { 126 return err 127 } 128 129 nd.tmpSendLitMsg(msg) 130 131 return nil 132 } 133 134 func (nd *LitNode) AcceptDlc(cIdx uint64) error { 135 c, err := nd.DlcManager.LoadContract(cIdx) 136 if err != nil { 137 return err 138 } 139 140 if c.Status != lnutil.ContractStatusOfferedToMe { 141 return fmt.Errorf("You cannot decline a contract unless it is in the 'Offered/Awaiting reply' state") 142 } 143 144 if !nd.ConnectedToPeer(c.PeerIdx) { 145 return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx) 146 } 147 148 // Preconditions checked - Go execute the acceptance in a separate go routine 149 // while returning the status back to the client 150 go func(nd *LitNode, c *lnutil.DlcContract) { 151 c.Status = lnutil.ContractStatusAccepting 152 nd.DlcManager.SaveContract(c) 153 154 // Fund the contract 155 err = nd.FundContract(c) 156 if err != nil { 157 c.Status = lnutil.ContractStatusError 158 nd.DlcManager.SaveContract(c) 159 return 160 } 161 162 var kg portxo.KeyGen 163 kg.Depth = 5 164 kg.Step[0] = 44 | 1<<31 165 kg.Step[1] = c.CoinType | 1<<31 166 kg.Step[2] = UseContractFundMultisig 167 kg.Step[3] = c.PeerIdx | 1<<31 168 kg.Step[4] = uint32(c.Idx) | 1<<31 169 170 c.OurFundMultisigPub, err = nd.GetUsePub(kg, UseContractFundMultisig) 171 if err != nil { 172 logging.Errorf("Error while getting multisig pubkey: %s", err.Error()) 173 c.Status = lnutil.ContractStatusError 174 nd.DlcManager.SaveContract(c) 175 return 176 } 177 178 c.OurPayoutBase, err = nd.GetUsePub(kg, UseContractPayoutBase) 179 if err != nil { 180 logging.Errorf("Error while getting payoutbase: %s", err.Error()) 181 c.Status = lnutil.ContractStatusError 182 nd.DlcManager.SaveContract(c) 183 return 184 } 185 186 ourPayoutPKHKey, err := nd.GetUsePub(kg, UseContractPayoutPKH) 187 if err != nil { 188 logging.Errorf("Error while getting our payout pubkey: %s", err.Error()) 189 c.Status = lnutil.ContractStatusError 190 nd.DlcManager.SaveContract(c) 191 return 192 } 193 copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) 194 195 // Now we can sign the division 196 sigs, err := nd.SignSettlementDivisions(c) 197 if err != nil { 198 logging.Errorf("Error signing settlement divisions: %s", err.Error()) 199 c.Status = lnutil.ContractStatusError 200 nd.DlcManager.SaveContract(c) 201 return 202 } 203 204 msg := lnutil.NewDlcOfferAcceptMsg(c, sigs) 205 c.Status = lnutil.ContractStatusAccepted 206 207 nd.DlcManager.SaveContract(c) 208 nd.tmpSendLitMsg(msg) 209 }(nd, c) 210 return nil 211 } 212 213 func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { 214 c := new(lnutil.DlcContract) 215 216 c.PeerIdx = peer.Idx 217 c.Status = lnutil.ContractStatusOfferedToMe 218 // Reverse copy from the contract we received 219 c.OurFundingAmount = msg.Contract.TheirFundingAmount 220 c.TheirFundingAmount = msg.Contract.OurFundingAmount 221 c.OurFundingInputs = msg.Contract.TheirFundingInputs 222 c.TheirFundingInputs = msg.Contract.OurFundingInputs 223 c.OurFundMultisigPub = msg.Contract.TheirFundMultisigPub 224 c.TheirFundMultisigPub = msg.Contract.OurFundMultisigPub 225 c.OurPayoutBase = msg.Contract.TheirPayoutBase 226 c.TheirPayoutBase = msg.Contract.OurPayoutBase 227 c.OurChangePKH = msg.Contract.TheirChangePKH 228 c.TheirChangePKH = msg.Contract.OurChangePKH 229 c.TheirIdx = msg.Contract.Idx 230 231 c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division)) 232 for i := 0; i < len(msg.Contract.Division); i++ { 233 c.Division[i].OracleValue = msg.Contract.Division[i].OracleValue 234 c.Division[i].ValueOurs = (c.TheirFundingAmount + c.OurFundingAmount) - msg.Contract.Division[i].ValueOurs 235 } 236 237 // Copy 238 c.CoinType = msg.Contract.CoinType 239 c.OracleA = msg.Contract.OracleA 240 c.OracleR = msg.Contract.OracleR 241 c.OracleTimestamp = msg.Contract.OracleTimestamp 242 243 err := nd.DlcManager.SaveContract(c) 244 if err != nil { 245 logging.Errorf("DlcOfferHandler SaveContract err %s\n", err.Error()) 246 return 247 } 248 249 _, ok := nd.SubWallet[msg.Contract.CoinType] 250 if !ok { 251 // We don't have this coin type, automatically decline 252 nd.DeclineDlc(c.Idx, 0x02) 253 } 254 255 } 256 257 func (nd *LitNode) DlcDeclineHandler(msg lnutil.DlcOfferDeclineMsg, peer *RemotePeer) { 258 c, err := nd.DlcManager.LoadContract(msg.Idx) 259 if err != nil { 260 logging.Errorf("DlcDeclineHandler FindContract err %s\n", err.Error()) 261 return 262 } 263 264 c.Status = lnutil.ContractStatusDeclined 265 err = nd.DlcManager.SaveContract(c) 266 if err != nil { 267 logging.Errorf("DlcDeclineHandler SaveContract err %s\n", err.Error()) 268 return 269 } 270 } 271 272 func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePeer) error { 273 c, err := nd.DlcManager.LoadContract(msg.Idx) 274 if err != nil { 275 logging.Errorf("DlcAcceptHandler FindContract err %s\n", err.Error()) 276 return err 277 } 278 279 // TODO: Check signatures 280 281 c.TheirChangePKH = msg.OurChangePKH 282 c.TheirFundingInputs = msg.FundingInputs 283 c.TheirSettlementSignatures = msg.SettlementSignatures 284 c.TheirFundMultisigPub = msg.OurFundMultisigPub 285 c.TheirPayoutBase = msg.OurPayoutBase 286 c.TheirPayoutPKH = msg.OurPayoutPKH 287 c.TheirIdx = msg.OurIdx 288 289 c.Status = lnutil.ContractStatusAccepted 290 err = nd.DlcManager.SaveContract(c) 291 if err != nil { 292 logging.Errorf("DlcAcceptHandler SaveContract err %s\n", err.Error()) 293 return err 294 } 295 296 // create our settlement signatures and ack 297 sigs, err := nd.SignSettlementDivisions(c) 298 if err != nil { 299 return err 300 } 301 302 outMsg := lnutil.NewDlcContractAckMsg(c, sigs) 303 c.Status = lnutil.ContractStatusAcknowledged 304 305 err = nd.DlcManager.SaveContract(c) 306 if err != nil { 307 return err 308 } 309 310 nd.tmpSendLitMsg(outMsg) 311 312 return nil 313 314 } 315 316 func (nd *LitNode) DlcContractAckHandler(msg lnutil.DlcContractAckMsg, peer *RemotePeer) { 317 c, err := nd.DlcManager.LoadContract(msg.Idx) 318 if err != nil { 319 logging.Errorf("DlcContractAckHandler FindContract err %s\n", err.Error()) 320 return 321 } 322 323 // TODO: Check signatures 324 325 c.Status = lnutil.ContractStatusAcknowledged 326 327 err = nd.DlcManager.SaveContract(c) 328 if err != nil { 329 logging.Errorf("DlcContractAckHandler SaveContract err %s\n", err.Error()) 330 return 331 } 332 333 // We have everything now, send our signatures to the funding TX 334 wal, ok := nd.SubWallet[c.CoinType] 335 if !ok { 336 logging.Errorf("DlcContractAckHandler No wallet for cointype %d\n", c.CoinType) 337 return 338 } 339 340 tx, err := nd.BuildDlcFundingTransaction(c) 341 if err != nil { 342 logging.Errorf("DlcContractAckHandler BuildDlcFundingTransaction err %s\n", err.Error()) 343 return 344 } 345 346 err = wal.SignMyInputs(&tx) 347 if err != nil { 348 logging.Errorf("DlcContractAckHandler SignMyInputs err %s\n", err.Error()) 349 return 350 } 351 352 outMsg := lnutil.NewDlcContractFundingSigsMsg(c, &tx) 353 354 nd.tmpSendLitMsg(outMsg) 355 } 356 357 func (nd *LitNode) DlcFundingSigsHandler(msg lnutil.DlcContractFundingSigsMsg, peer *RemotePeer) { 358 c, err := nd.DlcManager.LoadContract(msg.Idx) 359 if err != nil { 360 logging.Errorf("DlcFundingSigsHandler FindContract err %s\n", err.Error()) 361 return 362 } 363 364 // TODO: Check signatures 365 366 // We have everything now. Sign our inputs to the funding TX and send it to the blockchain. 367 wal, ok := nd.SubWallet[c.CoinType] 368 if !ok { 369 logging.Errorf("DlcFundingSigsHandler No wallet for cointype %d\n", c.CoinType) 370 return 371 } 372 373 wal.SignMyInputs(msg.SignedFundingTx) 374 375 wal.DirectSendTx(msg.SignedFundingTx) 376 377 err = wal.WatchThis(c.FundingOutpoint) 378 if err != nil { 379 logging.Errorf("DlcFundingSigsHandler WatchThis err %s\n", err.Error()) 380 return 381 } 382 383 c.Status = lnutil.ContractStatusActive 384 err = nd.DlcManager.SaveContract(c) 385 if err != nil { 386 logging.Errorf("DlcFundingSigsHandler SaveContract err %s\n", err.Error()) 387 return 388 } 389 390 outMsg := lnutil.NewDlcContractSigProofMsg(c, msg.SignedFundingTx) 391 392 nd.tmpSendLitMsg(outMsg) 393 } 394 395 func (nd *LitNode) DlcSigProofHandler(msg lnutil.DlcContractSigProofMsg, peer *RemotePeer) { 396 c, err := nd.DlcManager.LoadContract(msg.Idx) 397 if err != nil { 398 logging.Errorf("DlcSigProofHandler FindContract err %s\n", err.Error()) 399 return 400 } 401 402 // TODO: Check signatures 403 wal, ok := nd.SubWallet[c.CoinType] 404 if !ok { 405 logging.Errorf("DlcSigProofHandler No wallet for cointype %d\n", c.CoinType) 406 return 407 } 408 409 err = wal.WatchThis(c.FundingOutpoint) 410 if err != nil { 411 logging.Errorf("DlcSigProofHandler WatchThis err %s\n", err.Error()) 412 return 413 } 414 415 c.Status = lnutil.ContractStatusActive 416 err = nd.DlcManager.SaveContract(c) 417 if err != nil { 418 logging.Errorf("DlcSigProofHandler SaveContract err %s\n", err.Error()) 419 return 420 } 421 } 422 423 func (nd *LitNode) SignSettlementDivisions(c *lnutil.DlcContract) ([]lnutil.DlcContractSettlementSignature, error) { 424 wal, ok := nd.SubWallet[c.CoinType] 425 if !ok { 426 return nil, fmt.Errorf("Wallet of type %d not found", c.CoinType) 427 } 428 429 var kg portxo.KeyGen 430 kg.Depth = 5 431 kg.Step[0] = 44 | 1<<31 432 kg.Step[1] = c.CoinType | 1<<31 433 kg.Step[2] = UseContractFundMultisig 434 kg.Step[3] = c.PeerIdx | 1<<31 435 kg.Step[4] = uint32(c.Idx) | 1<<31 436 437 priv, err := wal.GetPriv(kg) 438 if err != nil { 439 return nil, fmt.Errorf("Could not get private key for contract %d", c.Idx) 440 } 441 442 fundingTx, err := nd.BuildDlcFundingTransaction(c) 443 if err != nil { 444 return nil, err 445 } 446 447 c.FundingOutpoint = wire.OutPoint{Hash: fundingTx.TxHash(), Index: 0} 448 449 returnValue := make([]lnutil.DlcContractSettlementSignature, len(c.Division)) 450 for i, d := range c.Division { 451 tx, err := lnutil.SettlementTx(c, d, true) 452 if err != nil { 453 return nil, err 454 } 455 sig, err := nd.SignSettlementTx(c, tx, priv) 456 if err != nil { 457 return nil, err 458 } 459 returnValue[i].Outcome = d.OracleValue 460 returnValue[i].Signature = sig 461 } 462 463 return returnValue, nil 464 } 465 466 func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx, error) { 467 // make the tx 468 tx := wire.NewMsgTx() 469 470 // set version 2, for op_csv 471 tx.Version = 2 472 473 // add all the txins 474 var ourInputTotal int64 475 var theirInputTotal int64 476 477 for _, u := range c.OurFundingInputs { 478 tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil)) 479 ourInputTotal += u.Value 480 } 481 for _, u := range c.TheirFundingInputs { 482 tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil)) 483 theirInputTotal += u.Value 484 } 485 486 // add change and sort 487 tx.AddTxOut(wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH))) 488 tx.AddTxOut(wire.NewTxOut(ourInputTotal-c.OurFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.OurChangePKH))) 489 490 txsort.InPlaceSort(tx) 491 492 // get txo for channel 493 txo, err := lnutil.FundTxOut(c.TheirFundMultisigPub, c.OurFundMultisigPub, c.OurFundingAmount+c.TheirFundingAmount) 494 if err != nil { 495 return *tx, err 496 } 497 498 // Ensure contract funding output is always at position 0 499 txos := make([]*wire.TxOut, len(tx.TxOut)+1) 500 txos[0] = txo 501 copy(txos[1:], tx.TxOut) 502 tx.TxOut = txos 503 504 return *tx, nil 505 506 } 507 508 func (nd *LitNode) FundContract(c *lnutil.DlcContract) error { 509 wal, ok := nd.SubWallet[c.CoinType] 510 if !ok { 511 return fmt.Errorf("No wallet of type %d connected", c.CoinType) 512 } 513 514 utxos, _, err := wal.PickUtxos(int64(c.OurFundingAmount), 500, wal.Fee(), true) 515 if err != nil { 516 return err 517 } 518 519 c.OurFundingInputs = make([]lnutil.DlcContractFundingInput, len(utxos)) 520 for i := 0; i < len(utxos); i++ { 521 c.OurFundingInputs[i] = lnutil.DlcContractFundingInput{Outpoint: utxos[i].Op, Value: utxos[i].Value} 522 } 523 524 c.OurChangePKH, err = wal.NewAdr() 525 if err != nil { 526 return err 527 } 528 529 return nil 530 } 531 532 func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32]byte) ([32]byte, [32]byte, error) { 533 534 c, err := nd.DlcManager.LoadContract(cIdx) 535 if err != nil { 536 logging.Errorf("SettleContract FindContract err %s\n", err.Error()) 537 return [32]byte{}, [32]byte{}, err 538 } 539 540 c.Status = lnutil.ContractStatusSettling 541 err = nd.DlcManager.SaveContract(c) 542 if err != nil { 543 logging.Errorf("SettleContract SaveContract err %s\n", err.Error()) 544 return [32]byte{}, [32]byte{}, err 545 } 546 547 d, err := c.GetDivision(oracleValue) 548 if err != nil { 549 logging.Errorf("SettleContract GetDivision err %s\n", err.Error()) 550 return [32]byte{}, [32]byte{}, err 551 } 552 553 wal, ok := nd.SubWallet[c.CoinType] 554 if !ok { 555 return [32]byte{}, [32]byte{}, fmt.Errorf("SettleContract Wallet of type %d not found", c.CoinType) 556 } 557 558 var kg portxo.KeyGen 559 kg.Depth = 5 560 kg.Step[0] = 44 | 1<<31 561 kg.Step[1] = c.CoinType | 1<<31 562 kg.Step[2] = UseContractFundMultisig 563 kg.Step[3] = c.PeerIdx | 1<<31 564 kg.Step[4] = uint32(c.Idx) | 1<<31 565 566 priv, err := wal.GetPriv(kg) 567 if err != nil { 568 return [32]byte{}, [32]byte{}, fmt.Errorf("SettleContract Could not get private key for contract %d", c.Idx) 569 } 570 571 settleTx, err := lnutil.SettlementTx(c, *d, false) 572 if err != nil { 573 logging.Errorf("SettleContract SettlementTx err %s\n", err.Error()) 574 return [32]byte{}, [32]byte{}, err 575 } 576 577 mySig, err := nd.SignSettlementTx(c, settleTx, priv) 578 if err != nil { 579 logging.Errorf("SettleContract SignSettlementTx err %s", err.Error()) 580 return [32]byte{}, [32]byte{}, err 581 } 582 583 myBigSig := sig64.SigDecompress(mySig) 584 585 theirSig, err := c.GetTheirSettlementSignature(oracleValue) 586 theirBigSig := sig64.SigDecompress(theirSig) 587 588 // put the sighash all byte on the end of both signatures 589 myBigSig = append(myBigSig, byte(txscript.SigHashAll)) 590 theirBigSig = append(theirBigSig, byte(txscript.SigHashAll)) 591 592 pre, swap, err := lnutil.FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) 593 if err != nil { 594 logging.Errorf("SettleContract FundTxScript err %s", err.Error()) 595 return [32]byte{}, [32]byte{}, err 596 } 597 598 // swap if needed 599 if swap { 600 settleTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirBigSig, myBigSig) 601 } else { 602 settleTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, myBigSig, theirBigSig) 603 } 604 605 // Settlement TX should be valid here, so publish it. 606 err = wal.DirectSendTx(settleTx) 607 if err != nil { 608 logging.Errorf("SettleContract DirectSendTx (settle) err %s", err.Error()) 609 return [32]byte{}, [32]byte{}, err 610 } 611 612 // TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime. 613 txClaim := wire.NewMsgTx() 614 txClaim.Version = 2 615 616 settleOutpoint := wire.OutPoint{Hash: settleTx.TxHash(), Index: 0} 617 txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) 618 619 addr, err := wal.NewAdr() 620 txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-1000, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX 621 622 kg.Step[2] = UseContractPayoutBase 623 privSpend, _ := wal.GetPriv(kg) 624 625 pubSpend := wal.GetPub(kg) 626 privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oracleSig[:]) 627 privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle) 628 629 var pubOracleBytes [33]byte 630 copy(pubOracleBytes[:], pubOracle.SerializeCompressed()) 631 var pubSpendBytes [33]byte 632 copy(pubSpendBytes[:], pubSpend.SerializeCompressed()) 633 634 settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, pubOracleBytes, c.TheirPayoutBase, 5) 635 err = nd.SignClaimTx(txClaim, settleTx.TxOut[0].Value, settleScript, privContractOutput, false) 636 if err != nil { 637 logging.Errorf("SettleContract SignClaimTx err %s", err.Error()) 638 return [32]byte{}, [32]byte{}, err 639 } 640 641 // Claim TX should be valid here, so publish it. 642 err = wal.DirectSendTx(txClaim) 643 if err != nil { 644 logging.Errorf("SettleContract DirectSendTx (claim) err %s", err.Error()) 645 return [32]byte{}, [32]byte{}, err 646 } 647 648 c.Status = lnutil.ContractStatusClosed 649 err = nd.DlcManager.SaveContract(c) 650 if err != nil { 651 return [32]byte{}, [32]byte{}, err 652 } 653 return settleTx.TxHash(), txClaim.TxHash(), nil 654 }