github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/lnutil/dlclib.go (about) 1 package lnutil 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/binary" 7 "fmt" 8 "math/big" 9 10 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 11 "github.com/mit-dci/lit/consts" 12 "github.com/mit-dci/lit/crypto/koblitz" 13 "github.com/mit-dci/lit/logging" 14 "github.com/mit-dci/lit/wire" 15 ) 16 17 // DlcContractStatus is an enumeration containing the various statuses a 18 // contract can have 19 type DlcContractStatus int 20 21 const ( 22 ContractStatusDraft DlcContractStatus = 0 23 ContractStatusOfferedByMe DlcContractStatus = 1 24 ContractStatusOfferedToMe DlcContractStatus = 2 25 ContractStatusDeclined DlcContractStatus = 3 26 ContractStatusAccepted DlcContractStatus = 4 27 ContractStatusAcknowledged DlcContractStatus = 5 28 ContractStatusActive DlcContractStatus = 6 29 ContractStatusSettling DlcContractStatus = 7 30 ContractStatusClosed DlcContractStatus = 8 31 ContractStatusError DlcContractStatus = 9 32 ContractStatusAccepting DlcContractStatus = 10 33 ) 34 35 // scalarSize is the size of an encoded big endian scalar. 36 const scalarSize = 32 37 38 // DlcContract is a struct containing all elements to work with a Discreet 39 // Log Contract. This struct is stored in the database 40 type DlcContract struct { 41 // Index of the contract for referencing in commands 42 Idx uint64 43 // Index of the contract on the other peer (so we can reference it in 44 // messages) 45 TheirIdx uint64 46 // Index of the peer we've offered the contract to or received the contract 47 // from 48 PeerIdx uint32 49 // Coin type 50 CoinType uint32 51 // Pub keys of the oracle and the R point used in the contract 52 OracleA, OracleR [33]byte 53 // The time we expect the oracle to publish 54 OracleTimestamp uint64 55 // The payout specification 56 Division []DlcContractDivision 57 // The amounts either side are funding 58 OurFundingAmount, TheirFundingAmount int64 59 // PKH to which the contracts funding change should go 60 OurChangePKH, TheirChangePKH [20]byte 61 // Pubkey used in the funding multisig output 62 OurFundMultisigPub, TheirFundMultisigPub [33]byte 63 // Pubkey to be used in the commit script (combined with oracle pubkey 64 // or CSV timeout) 65 OurPayoutBase, TheirPayoutBase [33]byte 66 // Pubkeyhash to which the contract pays out (directly) 67 OurPayoutPKH, TheirPayoutPKH [20]byte 68 // Status of the contract 69 Status DlcContractStatus 70 // Outpoints used to fund the contract 71 OurFundingInputs, TheirFundingInputs []DlcContractFundingInput 72 // Signatures for the settlement transactions 73 TheirSettlementSignatures []DlcContractSettlementSignature 74 // The outpoint of the funding TX we want to spend in the settlement 75 // for easier monitoring 76 FundingOutpoint wire.OutPoint 77 } 78 79 // DlcContractDivision describes a single division of the contract. If the 80 // oracle predicts OracleValue, we receive ValueOurs 81 type DlcContractDivision struct { 82 OracleValue int64 83 ValueOurs int64 84 } 85 86 // DlcContractFundingInput describes a UTXO that is offered to fund the 87 // contract with 88 type DlcContractFundingInput struct { 89 Outpoint wire.OutPoint 90 Value int64 91 } 92 93 // DlcContractFromBytes deserializes a byte array back into a DlcContract struct 94 func DlcContractFromBytes(b []byte) (*DlcContract, error) { 95 buf := bytes.NewBuffer(b) 96 c := new(DlcContract) 97 98 ourIdx, err := wire.ReadVarInt(buf, 0) 99 if err != nil { 100 logging.Errorf("Error while deserializing varint for theirIdx: %s", err.Error()) 101 return nil, err 102 } 103 c.Idx = ourIdx 104 105 theirIdx, err := wire.ReadVarInt(buf, 0) 106 if err != nil { 107 logging.Errorf("Error while deserializing varint for theirIdx: %s", err.Error()) 108 return nil, err 109 } 110 c.TheirIdx = theirIdx 111 112 copy(c.OracleA[:], buf.Next(33)) 113 copy(c.OracleR[:], buf.Next(33)) 114 115 peerIdx, err := wire.ReadVarInt(buf, 0) 116 if err != nil { 117 logging.Errorf("Error while deserializing varint for peerIdx: %s", err.Error()) 118 return nil, err 119 } 120 c.PeerIdx = uint32(peerIdx) 121 122 coinType, err := wire.ReadVarInt(buf, 0) 123 if err != nil { 124 logging.Errorf("Error while deserializing varint for coinType: %s", err.Error()) 125 return nil, err 126 } 127 c.CoinType = uint32(coinType) 128 c.OracleTimestamp, err = wire.ReadVarInt(buf, 0) 129 if err != nil { 130 return nil, err 131 } 132 ourFundingAmount, err := wire.ReadVarInt(buf, 0) 133 if err != nil { 134 return nil, err 135 } 136 c.OurFundingAmount = int64(ourFundingAmount) 137 theirFundingAmount, err := wire.ReadVarInt(buf, 0) 138 if err != nil { 139 return nil, err 140 } 141 c.TheirFundingAmount = int64(theirFundingAmount) 142 143 copy(c.OurChangePKH[:], buf.Next(20)) 144 copy(c.TheirChangePKH[:], buf.Next(20)) 145 146 copy(c.OurFundMultisigPub[:], buf.Next(33)) 147 copy(c.TheirFundMultisigPub[:], buf.Next(33)) 148 149 copy(c.OurPayoutBase[:], buf.Next(33)) 150 copy(c.TheirPayoutBase[:], buf.Next(33)) 151 152 copy(c.OurPayoutPKH[:], buf.Next(20)) 153 copy(c.TheirPayoutPKH[:], buf.Next(20)) 154 155 status, err := wire.ReadVarInt(buf, 0) 156 if err != nil { 157 logging.Errorf("Error while deserializing varint for status: %s", err.Error()) 158 return nil, err 159 } 160 161 c.Status = DlcContractStatus(status) 162 163 ourInputsLen, err := wire.ReadVarInt(buf, 0) 164 if err != nil { 165 return nil, err 166 } 167 168 c.OurFundingInputs = make([]DlcContractFundingInput, ourInputsLen) 169 var op [36]byte 170 for i := uint64(0); i < ourInputsLen; i++ { 171 copy(op[:], buf.Next(36)) 172 c.OurFundingInputs[i].Outpoint = *OutPointFromBytes(op) 173 inputValue, err := wire.ReadVarInt(buf, 0) 174 if err != nil { 175 return nil, err 176 } 177 c.OurFundingInputs[i].Value = int64(inputValue) 178 } 179 180 theirInputsLen, err := wire.ReadVarInt(buf, 0) 181 if err != nil { 182 return nil, err 183 } 184 185 c.TheirFundingInputs = make([]DlcContractFundingInput, theirInputsLen) 186 for i := uint64(0); i < theirInputsLen; i++ { 187 copy(op[:], buf.Next(36)) 188 c.TheirFundingInputs[i].Outpoint = *OutPointFromBytes(op) 189 inputValue, err := wire.ReadVarInt(buf, 0) 190 if err != nil { 191 192 return nil, err 193 } 194 c.TheirFundingInputs[i].Value = int64(inputValue) 195 } 196 197 divisionLen, err := wire.ReadVarInt(buf, 0) 198 if err != nil { 199 return nil, err 200 } 201 202 c.Division = make([]DlcContractDivision, divisionLen) 203 for i := uint64(0); i < divisionLen; i++ { 204 oracleValue, err := wire.ReadVarInt(buf, 0) 205 if err != nil { 206 return nil, err 207 } 208 valueOurs, err := wire.ReadVarInt(buf, 0) 209 if err != nil { 210 return nil, err 211 } 212 c.Division[i].OracleValue = int64(oracleValue) 213 c.Division[i].ValueOurs = int64(valueOurs) 214 } 215 216 theirSigCount, err := wire.ReadVarInt(buf, 0) 217 if err != nil { 218 return nil, err 219 } 220 c.TheirSettlementSignatures = make([]DlcContractSettlementSignature, 221 theirSigCount) 222 223 for i := uint64(0); i < theirSigCount; i++ { 224 outcome, err := wire.ReadVarInt(buf, 0) 225 if err != nil { 226 return nil, err 227 } 228 c.TheirSettlementSignatures[i].Outcome = int64(outcome) 229 copy(c.TheirSettlementSignatures[i].Signature[:], buf.Next(64)) 230 } 231 232 copy(op[:], buf.Next(36)) 233 c.FundingOutpoint = *OutPointFromBytes(op) 234 235 return c, nil 236 } 237 238 // Bytes serializes a DlcContract struct into a byte array 239 func (self *DlcContract) Bytes() []byte { 240 var buf bytes.Buffer 241 242 wire.WriteVarInt(&buf, 0, uint64(self.Idx)) 243 wire.WriteVarInt(&buf, 0, uint64(self.TheirIdx)) 244 buf.Write(self.OracleA[:]) 245 buf.Write(self.OracleR[:]) 246 wire.WriteVarInt(&buf, 0, uint64(self.PeerIdx)) 247 wire.WriteVarInt(&buf, 0, uint64(self.CoinType)) 248 wire.WriteVarInt(&buf, 0, uint64(self.OracleTimestamp)) 249 wire.WriteVarInt(&buf, 0, uint64(self.OurFundingAmount)) 250 wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingAmount)) 251 252 buf.Write(self.OurChangePKH[:]) 253 buf.Write(self.TheirChangePKH[:]) 254 buf.Write(self.OurFundMultisigPub[:]) 255 buf.Write(self.TheirFundMultisigPub[:]) 256 buf.Write(self.OurPayoutBase[:]) 257 buf.Write(self.TheirPayoutBase[:]) 258 buf.Write(self.OurPayoutPKH[:]) 259 buf.Write(self.TheirPayoutPKH[:]) 260 261 var status = uint64(self.Status) 262 wire.WriteVarInt(&buf, 0, status) 263 264 ourInputsLen := uint64(len(self.OurFundingInputs)) 265 wire.WriteVarInt(&buf, 0, ourInputsLen) 266 267 for i := 0; i < len(self.OurFundingInputs); i++ { 268 opArr := OutPointToBytes(self.OurFundingInputs[i].Outpoint) 269 buf.Write(opArr[:]) 270 wire.WriteVarInt(&buf, 0, uint64(self.OurFundingInputs[i].Value)) 271 } 272 273 theirInputsLen := uint64(len(self.TheirFundingInputs)) 274 wire.WriteVarInt(&buf, 0, theirInputsLen) 275 276 for i := 0; i < len(self.TheirFundingInputs); i++ { 277 opArr := OutPointToBytes(self.TheirFundingInputs[i].Outpoint) 278 buf.Write(opArr[:]) 279 wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingInputs[i].Value)) 280 } 281 282 divisionLen := uint64(len(self.Division)) 283 wire.WriteVarInt(&buf, 0, divisionLen) 284 285 for i := 0; i < len(self.Division); i++ { 286 wire.WriteVarInt(&buf, 0, uint64(self.Division[i].OracleValue)) 287 wire.WriteVarInt(&buf, 0, uint64(self.Division[i].ValueOurs)) 288 } 289 290 theirSigLen := uint64(len(self.TheirSettlementSignatures)) 291 wire.WriteVarInt(&buf, 0, theirSigLen) 292 293 for i := 0; i < len(self.TheirSettlementSignatures); i++ { 294 outcome := uint64(self.TheirSettlementSignatures[i].Outcome) 295 wire.WriteVarInt(&buf, 0, outcome) 296 buf.Write(self.TheirSettlementSignatures[i].Signature[:]) 297 } 298 299 opArr := OutPointToBytes(self.FundingOutpoint) 300 buf.Write(opArr[:]) 301 302 return buf.Bytes() 303 } 304 305 // GetDivision loops over all division specifications inside the contract and 306 // returns the one matching the requested oracle value 307 func (c DlcContract) GetDivision(value int64) (*DlcContractDivision, error) { 308 for _, d := range c.Division { 309 if d.OracleValue == value { 310 return &d, nil 311 } 312 } 313 314 return nil, fmt.Errorf("Division not found in contract") 315 } 316 317 // GetTheirSettlementSignature loops over all stored settlement signatures from 318 // the counter party and returns the one matching the requested oracle value 319 func (c DlcContract) GetTheirSettlementSignature(val int64) ([64]byte, error) { 320 321 for _, s := range c.TheirSettlementSignatures { 322 if s.Outcome == val { 323 return s.Signature, nil 324 } 325 } 326 327 return [64]byte{}, fmt.Errorf("Signature not found in contract") 328 } 329 330 // PrintTx prints out a transaction as serialized byte array to StdOut 331 func PrintTx(tx *wire.MsgTx) { 332 var buf bytes.Buffer 333 w := bufio.NewWriter(&buf) 334 tx.Serialize(w) 335 w.Flush() 336 logging.Infof("%x\n", buf.Bytes()) 337 } 338 339 // DlcOutput returns a Txo for a particular value that pays to 340 // (PubKeyPeer+PubKeyOracleSig or (OurPubKey and TimeDelay)) 341 func DlcOutput(pkPeer, pkOracleSig, pkOurs [33]byte, value int64) *wire.TxOut { 342 scriptBytes := DlcCommitScript(pkPeer, pkOracleSig, pkOurs, 5) 343 scriptBytes = P2WSHify(scriptBytes) 344 345 return wire.NewTxOut(value, scriptBytes) 346 } 347 348 // DlcCommitScript makes a script that pays to (PubKeyPeer+PubKeyOracleSig or 349 // (OurPubKey and TimeDelay)). We send this over (signed) to the other side. If 350 // they publish the TX with the correct script they can use the oracle's 351 // signature and their own private key to claim the funds from the output. 352 // However, if they send the wrong one, they won't be able to claim the funds 353 // and we can claim them once the time delay has passed. 354 func DlcCommitScript(pubKeyPeer, pubKeyOracleSig, ourPubKey [33]byte, 355 delay uint16) []byte { 356 // Combine pubKey and Oracle Sig 357 combinedPubKey := CombinePubs(pubKeyPeer, pubKeyOracleSig) 358 return CommitScript(combinedPubKey, ourPubKey, delay) 359 } 360 361 // BigIntToEncodedBytes converts a big integer into its corresponding 362 // 32 byte big endian representation. 363 func BigIntToEncodedBytes(a *big.Int) *[32]byte { 364 s := new([32]byte) 365 if a == nil { 366 return s 367 } 368 // Caveat: a can be longer than 32 bytes. 369 aB := a.Bytes() 370 371 // If we have a short byte string, expand 372 // it so that it's long enough. 373 aBLen := len(aB) 374 if aBLen < scalarSize { 375 diff := scalarSize - aBLen 376 for i := 0; i < diff; i++ { 377 aB = append([]byte{0x00}, aB...) 378 } 379 } 380 381 for i := 0; i < scalarSize; i++ { 382 s[i] = aB[i] 383 } 384 385 return s 386 } 387 388 // DlcCalcOracleSignaturePubKey computes the predicted signature s*G 389 // it's just R - h(R||m)A 390 func DlcCalcOracleSignaturePubKey(msg []byte, oracleA, 391 oracleR [33]byte) ([33]byte, error) { 392 return computePubKey(oracleA, oracleR, msg) 393 } 394 395 // calculates P = pubR - h(msg, pubR)pubA 396 func computePubKey(pubA, pubR [33]byte, msg []byte) ([33]byte, error) { 397 var returnValue [33]byte 398 399 // Hardcode curve 400 curve := koblitz.S256() 401 402 A, err := koblitz.ParsePubKey(pubA[:], curve) 403 if err != nil { 404 return returnValue, err 405 } 406 407 R, err := koblitz.ParsePubKey(pubR[:], curve) 408 if err != nil { 409 return returnValue, err 410 } 411 412 // e = Hash(messageType, oraclePubQ) 413 var hashInput []byte 414 hashInput = append(msg, R.X.Bytes()...) 415 e := chainhash.HashB(hashInput) 416 417 bigE := new(big.Int).SetBytes(e) 418 419 if bigE.Cmp(curve.N) >= 0 { 420 return returnValue, fmt.Errorf("hash of (msg, pubR) too big") 421 } 422 423 // e * B 424 A.X, A.Y = curve.ScalarMult(A.X, A.Y, e) 425 426 A.Y.Neg(A.Y) 427 428 A.Y.Mod(A.Y, curve.P) 429 430 P := new(koblitz.PublicKey) 431 432 // add to R 433 P.X, P.Y = curve.Add(A.X, A.Y, R.X, R.Y) 434 copy(returnValue[:], P.SerializeCompressed()) 435 return returnValue, nil 436 } 437 438 // SettlementTx returns the transaction to settle the contract. ours = the one 439 // we generate & sign. Theirs (ours = false) = the one they generated, so we can 440 // use their sigs 441 func SettlementTx(c *DlcContract, d DlcContractDivision, 442 ours bool) (*wire.MsgTx, error) { 443 444 tx := wire.NewMsgTx() 445 // set version 2, for op_csv 446 tx.Version = 2 447 448 tx.AddTxIn(wire.NewTxIn(&c.FundingOutpoint, nil, nil)) 449 450 totalFee := int64(consts.DlcSettlementTxFee) // TODO: Calculate 451 feeEach := int64(float64(totalFee) / float64(2)) 452 feeOurs := feeEach 453 feeTheirs := feeEach 454 valueOurs := d.ValueOurs 455 // We don't have enough to pay for a fee. We get 0, our contract partner 456 // pays the rest of the fee 457 if valueOurs < feeEach { 458 feeOurs = valueOurs 459 valueOurs = 0 460 } else { 461 valueOurs = d.ValueOurs - feeOurs 462 } 463 totalContractValue := c.TheirFundingAmount + c.OurFundingAmount 464 valueTheirs := totalContractValue - d.ValueOurs 465 466 if valueTheirs < feeEach { 467 feeTheirs = valueTheirs 468 valueTheirs = 0 469 feeOurs = totalFee - feeTheirs 470 valueOurs = d.ValueOurs - feeOurs 471 } else { 472 valueTheirs -= feeTheirs 473 } 474 475 var buf bytes.Buffer 476 binary.Write(&buf, binary.BigEndian, uint64(0)) 477 binary.Write(&buf, binary.BigEndian, uint64(0)) 478 binary.Write(&buf, binary.BigEndian, uint64(0)) 479 binary.Write(&buf, binary.BigEndian, d.OracleValue) 480 oracleSigPub, err := DlcCalcOracleSignaturePubKey(buf.Bytes(), 481 c.OracleA, c.OracleR) 482 if err != nil { 483 return nil, err 484 } 485 486 // Ours = the one we generate & sign. Theirs (ours = false) = the one they 487 // generated, so we can use their sigs 488 if ours { 489 if valueTheirs > 0 { 490 tx.AddTxOut(DlcOutput(c.TheirPayoutBase, oracleSigPub, 491 c.OurPayoutBase, valueTheirs)) 492 } 493 494 if valueOurs > 0 { 495 tx.AddTxOut(wire.NewTxOut(valueOurs, 496 DirectWPKHScriptFromPKH(c.OurPayoutPKH))) 497 } 498 } else { 499 if valueOurs > 0 { 500 tx.AddTxOut(DlcOutput(c.OurPayoutBase, oracleSigPub, 501 c.TheirPayoutBase, valueOurs)) 502 } 503 504 if valueTheirs > 0 { 505 tx.AddTxOut(wire.NewTxOut(valueTheirs, 506 DirectWPKHScriptFromPKH(c.TheirPayoutPKH))) 507 } 508 } 509 510 return tx, nil 511 }