decred.org/dcrdex@v1.0.3/dex/networks/dcr/script.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package dcr 5 6 import ( 7 "bytes" 8 "crypto/sha256" 9 "encoding/binary" 10 "errors" 11 "fmt" 12 13 "decred.org/dcrdex/dex" 14 "decred.org/dcrdex/server/account" 15 "decred.org/dcrwallet/v4/wallet/txsizes" 16 "github.com/decred/dcrd/chaincfg/v3" 17 "github.com/decred/dcrd/dcrec" 18 "github.com/decred/dcrd/dcrutil/v4" 19 "github.com/decred/dcrd/txscript/v4" 20 "github.com/decred/dcrd/txscript/v4/stdaddr" 21 "github.com/decred/dcrd/txscript/v4/stdscript" 22 "github.com/decred/dcrd/wire" 23 ) 24 25 const ( 26 // MaxCLTVScriptNum is the largest usable value for a CLTV lockTime. This 27 // will actually be stored in a 5-byte ScriptNum since they have a sign bit, 28 // however, it is not 2^39-1 since the spending transaction's nLocktime is 29 // an unsigned 32-bit integer and it must be at least the CLTV value. This 30 // establishes a maximum lock time of February 7, 2106. Any later requires 31 // using a block height instead of a unix epoch time stamp. 32 MaxCLTVScriptNum = 1<<32 - 1 // 0xffff_ffff a.k.a. 2^32-1 33 34 // SecretHashSize is the byte-length of the hash of the secret key used in an 35 // atomic swap. 36 SecretHashSize = 32 37 38 // SecretKeySize is the byte-length of the secret key used in an atomic swap. 39 SecretKeySize = 32 40 41 // Size of serialized compressed public key. 42 pubkeyLength = 33 // Length of a serialized compressed pubkey. 43 44 // SwapContractSize is the worst case scenario size for a swap contract, 45 // which is the pk-script of the non-change output of an initialization 46 // transaction as used in execution of an atomic swap. 47 // See ExtractSwapDetails for a breakdown of the bytes. 48 SwapContractSize = 97 49 50 // TxInOverhead is the overhead for a wire.TxIn with a scriptSig length < 51 // 254. prefix (41 bytes) + ValueIn (8 bytes) + BlockHeight (4 bytes) + 52 // BlockIndex (4 bytes) + sig script var int (at least 1 byte) 53 TxInOverhead = 41 + 8 + 4 + 4 // 57 + at least 1 more 54 55 P2PKSigScriptSize = txsizes.RedeemP2PKSigScriptSize 56 57 P2PKHSigScriptSize = txsizes.RedeemP2PKHSigScriptSize 58 P2PKHInputSize = TxInOverhead + 1 + P2PKHSigScriptSize // 57 + 1 + 108 = 166 59 60 // P2SHSigScriptSize and P2SHInputSize do not include the redeem script size 61 // (unknown), which is concatenated on execution with the p2sh pkScript. 62 //P2SHSigScriptSize = txsizes.RedeemP2SHSigScriptSize // 110 + redeem script! 63 //P2SHInputSize = TxInOverhead + 1 + P2SHSigScriptSize // 57 + 1 + 110 = 168 + redeem script! 64 65 // TxOutOverhead is the overhead associated with a transaction output. 66 // 8 bytes value + 2 bytes version + at least 1 byte varint script size 67 TxOutOverhead = 8 + 2 + 1 68 69 P2PKHOutputSize = TxOutOverhead + txsizes.P2PKHPkScriptSize // 36 70 P2SHOutputSize = TxOutOverhead + txsizes.P2SHPkScriptSize // 34 71 72 // MsgTxOverhead is 4 bytes version (lower 2 bytes for the real transaction 73 // version and upper 2 bytes for the serialization type) + 4 bytes locktime 74 // + 4 bytes expiry + 3 bytes of varints for the number of transaction 75 // inputs (x2 for witness and prefix) and outputs 76 MsgTxOverhead = 4 + 4 + 4 + 3 // 15 77 78 // InitTxSizeBase is the size of a standard serialized atomic swap 79 // initialization transaction with one change output and no inputs. MsgTx 80 // overhead is 4 bytes version + 4 bytes locktime + 4 bytes expiry + 3 bytes 81 // of varints for the number of transaction inputs (x2 for witness and 82 // prefix) and outputs. There is one P2SH output with a 23 byte pkScript, 83 // and one P2PKH change output with a 25 byte pkScript. 84 InitTxSizeBase = MsgTxOverhead + P2PKHOutputSize + P2SHOutputSize // 15 + 36 + 34 = 85 85 86 // InitTxSize is InitTxBaseSize + 1 P2PKH input 87 InitTxSize = InitTxSizeBase + P2PKHInputSize // 85(83) + 166 = 251 88 89 // DERSigLength is the maximum length of a DER encoded signature. 90 DERSigLength = 73 91 92 // RedeemSwapSigScriptSize is the worst case (largest) serialize size 93 // of a transaction signature script that redeems atomic swap output contract. 94 // It is calculated as: 95 // 96 // - OP_DATA_73 97 // - 72 bytes DER signature + 1 byte sighash type 98 // - OP_DATA_33 99 // - 33 bytes serialized compressed pubkey 100 // - OP_DATA_32 101 // - 32 bytes secret key 102 // - OP_1 103 // - varint 97 => OP_PUSHDATA1(0x4c) + 0x61 104 // - 97 bytes contract script 105 RedeemSwapSigScriptSize = 1 + DERSigLength + 1 + pubkeyLength + 1 + 32 + 1 + 2 + SwapContractSize // 241 106 107 // RefundSigScriptSize is the worst case (largest) serialize size 108 // of a transaction input script that refunds a compressed P2PKH output. 109 // It is calculated as: 110 // 111 // - OP_DATA_73 112 // - 72 bytes DER signature + 1 byte sighash type 113 // - OP_DATA_33 114 // - 33 bytes serialized compressed pubkey 115 // - OP_0 116 // - varint 97 => OP_PUSHDATA1(0x4c) + 0x61 117 // - 97 bytes contract script 118 RefundSigScriptSize = 1 + DERSigLength + 1 + pubkeyLength + 1 + 2 + SwapContractSize // 208 119 120 // BondScriptSize is the maximum size of a DEX time-locked fidelity bond 121 // output script to which a bond P2SH pays: 122 // OP_DATA_4/5 (4/5 bytes lockTime) OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 OP_DATA_20 (20-byte pubkey hash160) OP_EQUALVERIFY OP_CHECKSIG 123 BondScriptSize = 1 + 5 + 1 + 1 + 1 + 1 + 1 + 20 + 1 + 1 // 33 124 125 // RedeemBondSigScriptSize is the worst case size of a fidelity bond 126 // signature script that spends a bond output. It includes a signature, a 127 // compressed pubkey, and the bond script. Each of said data pushes use an 128 // OP_DATA_ code. 129 RedeemBondSigScriptSize = 1 + DERSigLength + 1 + pubkeyLength + 1 + BondScriptSize // 142 130 131 // BondPushDataSize is the size of the nulldata in a bond commitment output: 132 // OP_RETURN <pushData: ver[2] | account_id[32] | lockTime[4] | pkh[20]> 133 BondPushDataSize = 2 + account.HashSize + 4 + 20 134 ) 135 136 // redeemP2SHTxSize calculates the size of the redeeming transaction for a 137 // P2SH transaction with the given sigScipt (or witness data) size. 138 func redeemP2SHTxSize(redeemSigScriptSize uint64) uint64 { 139 inputSize := TxInOverhead + redeemSigScriptSize 140 return MsgTxOverhead + inputSize + P2PKHOutputSize 141 } 142 143 // redeemSwapTxSize returns the size of a swap refund tx. 144 func redeemSwapTxSize() uint64 { 145 return redeemP2SHTxSize(RedeemSwapSigScriptSize) 146 } 147 148 // refundBondTxSize returns the size of a bond refund tx. 149 func refundBondTxSize() uint64 { 150 return redeemP2SHTxSize(RedeemBondSigScriptSize) 151 } 152 153 // minHTLCValue calculates the minimum value for the output of a chained 154 // P2SH -> P2WPKH transaction pair where the spending tx size is known. 155 func minHTLCValue(maxFeeRate, redeemTxSize uint64) uint64 { 156 // Reversing IsDustVal. 157 // totalSize adds some buffer for the spending transaction. 158 var outputSize uint64 = P2PKHOutputSize // larger of bonds p2sh output and refund's p2pkh output. 159 totalSize := outputSize + 165 160 minInitTxValue := maxFeeRate * totalSize * 3 161 162 // The minInitTxValue would get the bond tx accepted, but when we go to 163 // refund, we need that output to pass too, So let's add the fees for the 164 // refund transaction. 165 redeemFees := redeemTxSize * maxFeeRate 166 return minInitTxValue + redeemFees 167 } 168 169 // MinBondSize is the minimum bond size that avoids dust for a given max network 170 // fee rate. 171 func MinBondSize(maxFeeRate uint64) uint64 { 172 refundTxSize := refundBondTxSize() 173 return minHTLCValue(maxFeeRate, refundTxSize) 174 } 175 176 // MinLotSize is the minimum lot size that avoids dust for a given max network 177 // fee rate. 178 func MinLotSize(maxFeeRate uint64) uint64 { 179 redeemSize := redeemSwapTxSize() 180 return minHTLCValue(maxFeeRate, redeemSize) 181 } 182 183 // ScriptType is a bitmask with information about a pubkey script and 184 // possibly its redeem script. 185 type ScriptType uint8 186 187 const ( 188 ScriptP2PKH ScriptType = 1 << iota 189 ScriptP2PK 190 ScriptP2SH 191 ScriptStake 192 ScriptMultiSig 193 ScriptSigEdwards 194 ScriptSigSchnorr 195 ScriptUnsupported 196 ) 197 198 // ParseScriptType creates a ScriptType bitmask for a pkScript. 199 func ParseScriptType(scriptVersion uint16, pkScript []byte) ScriptType { 200 st := stdscript.DetermineScriptType(scriptVersion, pkScript) 201 return convertScriptType(st) 202 } 203 204 // IsP2SH will return boolean true if the script is a P2SH script. 205 func (s ScriptType) IsP2SH() bool { 206 return s&ScriptP2SH != 0 207 } 208 209 // IsStake will return boolean true if the pubkey script it tagged with a stake 210 // opcode. 211 func (s ScriptType) IsStake() bool { 212 return s&ScriptStake != 0 213 } 214 215 // IsP2PK will return boolean true if the script is a P2PK script. 216 func (s ScriptType) IsP2PK() bool { 217 return s&ScriptP2PK != 0 218 } 219 220 // IsP2PKH will return boolean true if the script is a P2PKH script. 221 func (s ScriptType) IsP2PKH() bool { 222 return s&ScriptP2PKH != 0 223 } 224 225 // IsMultiSig is whether the pkscript references a multi-sig redeem script. 226 // Since the DEX will know the redeem script, we can say whether it's multi-sig. 227 func (s ScriptType) IsMultiSig() bool { 228 return s&ScriptMultiSig != 0 229 } 230 231 func convertScriptType(st stdscript.ScriptType) ScriptType { 232 var scriptType ScriptType 233 switch st { 234 // P2PK 235 case stdscript.STPubKeyEcdsaSecp256k1: 236 scriptType = ScriptP2PK 237 case stdscript.STPubKeyEd25519: 238 scriptType = ScriptP2PK | ScriptSigEdwards 239 case stdscript.STPubKeySchnorrSecp256k1: 240 scriptType = ScriptP2PK | ScriptSigSchnorr 241 // P2PKH 242 case stdscript.STPubKeyHashEcdsaSecp256k1: 243 scriptType = ScriptP2PKH 244 case stdscript.STPubKeyHashEd25519: 245 scriptType = ScriptP2PKH | ScriptSigEdwards 246 case stdscript.STPubKeyHashSchnorrSecp256k1: 247 scriptType = ScriptP2PKH | ScriptSigSchnorr 248 case stdscript.STStakeSubmissionPubKeyHash, stdscript.STStakeChangePubKeyHash, 249 stdscript.STStakeGenPubKeyHash, stdscript.STStakeRevocationPubKeyHash, 250 stdscript.STTreasuryGenPubKeyHash: 251 scriptType = ScriptP2PKH | ScriptStake 252 // P2SH 253 case stdscript.STScriptHash: 254 scriptType = ScriptP2SH 255 case stdscript.STStakeChangeScriptHash, stdscript.STStakeGenScriptHash, 256 stdscript.STStakeRevocationScriptHash, stdscript.STStakeSubmissionScriptHash, 257 stdscript.STTreasuryGenScriptHash: 258 scriptType = ScriptP2SH | ScriptStake 259 // P2MS 260 case stdscript.STMultiSig: 261 scriptType = ScriptMultiSig 262 default: // STNonStandard, STNullData, STTreasuryAdd, etc 263 return ScriptUnsupported 264 } 265 return scriptType 266 } 267 268 // AddressScript returns the raw bytes of the address to be used when inserting 269 // the address into a txout's script. This is currently the address' pubkey or 270 // script hash160. Other address types are unsupported. 271 func AddressScript(addr stdaddr.Address) ([]byte, error) { 272 switch addr := addr.(type) { 273 case stdaddr.SerializedPubKeyer: 274 return addr.SerializedPubKey(), nil 275 case stdaddr.Hash160er: 276 return addr.Hash160()[:], nil 277 default: 278 return nil, fmt.Errorf("unsupported address type %T", addr) 279 } 280 } 281 282 // AddressSigType returns the digital signature algorithm for the specified 283 // address. 284 func AddressSigType(addr stdaddr.Address) (sigType dcrec.SignatureType, err error) { 285 switch addr.(type) { 286 case *stdaddr.AddressPubKeyEcdsaSecp256k1V0: 287 sigType = dcrec.STEcdsaSecp256k1 288 case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0: 289 sigType = dcrec.STEcdsaSecp256k1 290 291 case *stdaddr.AddressPubKeyEd25519V0: 292 sigType = dcrec.STEd25519 293 case *stdaddr.AddressPubKeyHashEd25519V0: 294 sigType = dcrec.STEd25519 295 296 case *stdaddr.AddressPubKeySchnorrSecp256k1V0: 297 sigType = dcrec.STSchnorrSecp256k1 298 case *stdaddr.AddressPubKeyHashSchnorrSecp256k1V0: 299 sigType = dcrec.STSchnorrSecp256k1 300 301 default: 302 err = fmt.Errorf("unsupported signature type") 303 } 304 return sigType, err 305 } 306 307 // MakeContract creates an atomic swap contract. The secretHash MUST be computed 308 // from a secret of length SecretKeySize bytes or the resulting contract will be 309 // invalid. 310 func MakeContract(recipient, sender string, secretHash []byte, lockTime int64, chainParams *chaincfg.Params) ([]byte, error) { 311 parseAddress := func(address string) (*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0, error) { 312 addr, err := stdaddr.DecodeAddress(address, chainParams) 313 if err != nil { 314 return nil, fmt.Errorf("error decoding address %s: %w", address, err) 315 } 316 if addr, ok := addr.(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0); ok { 317 return addr, nil 318 } else { 319 return nil, fmt.Errorf("address %s is not a pubkey-hash address or "+ 320 "signature algorithm is unsupported", address) 321 } 322 } 323 324 rAddr, err := parseAddress(recipient) 325 if err != nil { 326 return nil, fmt.Errorf("invalid recipient address: %w", err) 327 } 328 sAddr, err := parseAddress(sender) 329 if err != nil { 330 return nil, fmt.Errorf("invalid sender address: %w", err) 331 } 332 333 if len(secretHash) != SecretHashSize { 334 return nil, fmt.Errorf("secret hash of length %d not supported", len(secretHash)) 335 } 336 337 return txscript.NewScriptBuilder(). 338 AddOps([]byte{ 339 txscript.OP_IF, 340 txscript.OP_SIZE, 341 }).AddInt64(SecretKeySize). 342 AddOps([]byte{ 343 txscript.OP_EQUALVERIFY, 344 txscript.OP_SHA256, 345 }).AddData(secretHash). 346 AddOps([]byte{ 347 txscript.OP_EQUALVERIFY, 348 txscript.OP_DUP, 349 txscript.OP_HASH160, 350 }).AddData(rAddr.Hash160()[:]). 351 AddOp(txscript.OP_ELSE). 352 AddInt64(lockTime). 353 AddOps([]byte{ 354 txscript.OP_CHECKLOCKTIMEVERIFY, 355 txscript.OP_DROP, 356 txscript.OP_DUP, 357 txscript.OP_HASH160, 358 }).AddData(sAddr.Hash160()[:]). 359 AddOps([]byte{ 360 txscript.OP_ENDIF, 361 txscript.OP_EQUALVERIFY, 362 txscript.OP_CHECKSIG, 363 }).Script() 364 } 365 366 // RedeemP2SHContract returns the signature script to redeem a contract output 367 // using the redeemer's signature and the initiator's secret. This function 368 // assumes P2SH and appends the contract as the final data push. 369 func RedeemP2SHContract(contract, sig, pubkey, secret []byte) ([]byte, error) { 370 return txscript.NewScriptBuilder(). 371 AddData(sig). 372 AddData(pubkey). 373 AddData(secret). 374 AddInt64(1). 375 AddData(contract). 376 Script() 377 } 378 379 // RefundP2SHContract returns the signature script to refund a contract output 380 // using the contract author's signature after the locktime has been reached. 381 // This function assumes P2SH and appends the contract as the final data push. 382 func RefundP2SHContract(contract, sig, pubkey []byte) ([]byte, error) { 383 return txscript.NewScriptBuilder(). 384 AddData(sig). 385 AddData(pubkey). 386 AddInt64(0). 387 AddData(contract). 388 Script() 389 } 390 391 // OP_RETURN <pushData: ver[2] | account_id[32] | lockTime[4] | pkh[20]> 392 func extractBondCommitDataV0(pushData []byte) (acct account.AccountID, lockTime uint32, pubkeyHash [20]byte, err error) { 393 if len(pushData) < 2 { 394 err = errors.New("invalid data") 395 return 396 } 397 ver := binary.BigEndian.Uint16(pushData) 398 if ver != 0 { 399 err = fmt.Errorf("unexpected bond commitment version %d, expected 0", ver) 400 return 401 } 402 403 if len(pushData) != BondPushDataSize { 404 err = fmt.Errorf("invalid bond commitment output script length: %d", len(pushData)) 405 return 406 } 407 408 pushData = pushData[2:] // pop off ver 409 410 copy(acct[:], pushData) 411 pushData = pushData[account.HashSize:] 412 413 lockTime = binary.BigEndian.Uint32(pushData) 414 pushData = pushData[4:] 415 416 copy(pubkeyHash[:], pushData) 417 418 return 419 } 420 421 // ExtractBondCommitDataV0 parses a v0 bond commitment output script. This is 422 // the OP_RETURN output, not the P2SH bond output. Use ExtractBondDetailsV0 to 423 // parse the P2SH bond output's redeem script. 424 // 425 // If the decoded commitment data indicates a version other than 0, an error is 426 // returned. 427 func ExtractBondCommitDataV0(scriptVer uint16, pkScript []byte) (acct account.AccountID, lockTime uint32, pubkeyHash [20]byte, err error) { 428 tokenizer := txscript.MakeScriptTokenizer(scriptVer, pkScript) 429 if !tokenizer.Next() { 430 err = tokenizer.Err() 431 return 432 } 433 434 if tokenizer.Opcode() != txscript.OP_RETURN { 435 err = errors.New("not a null data output") 436 return 437 } 438 439 if !tokenizer.Next() { 440 err = tokenizer.Err() 441 return 442 } 443 444 pushData := tokenizer.Data() 445 acct, lockTime, pubkeyHash, err = extractBondCommitDataV0(pushData) 446 if err != nil { 447 return 448 } 449 450 if !tokenizer.Done() { 451 err = errors.New("script has extra opcodes") 452 return 453 } 454 455 return 456 } 457 458 // MakeBondScript constructs a versioned bond output script for the provided 459 // lock time and pubkey hash. Only version 0 is supported at present. The lock 460 // time must be less than 2^32-1 so that it uses at most 5 bytes. The lockTime 461 // is also required to use at least 4 bytes (time stamp, not block time). 462 func MakeBondScript(ver uint16, lockTime uint32, pubkeyHash []byte) ([]byte, error) { 463 if ver != 0 { 464 return nil, errors.New("only version 0 bonds supported") 465 } 466 if lockTime >= MaxCLTVScriptNum { // == should be OK, but let's not 467 return nil, errors.New("invalid lock time") 468 } 469 lockTimeBytes := txscript.ScriptNum(lockTime).Bytes() 470 if n := len(lockTimeBytes); n < 4 || n > 5 { 471 return nil, errors.New("invalid lock time") 472 } 473 if len(pubkeyHash) != 20 { 474 return nil, errors.New("invalid pubkey hash") 475 } 476 return txscript.NewScriptBuilder(). 477 AddData(lockTimeBytes). 478 AddOp(txscript.OP_CHECKLOCKTIMEVERIFY). 479 AddOp(txscript.OP_DROP). 480 AddOp(txscript.OP_DUP). 481 AddOp(txscript.OP_HASH160). 482 AddData(pubkeyHash). 483 AddOp(txscript.OP_EQUALVERIFY). 484 AddOp(txscript.OP_CHECKSIG). 485 Script() 486 } 487 488 // RefundBondScript builds the signature script to refund a time-locked fidelity 489 // bond in a P2SH output paying to the provided P2PKH bondScript. 490 func RefundBondScript(bondScript, sig, pubkey []byte) ([]byte, error) { 491 return txscript.NewScriptBuilder(). 492 AddData(sig). 493 AddData(pubkey). 494 AddData(bondScript). 495 Script() 496 } 497 498 // ExtractBondDetailsV0 validates the provided bond redeem script, extracting 499 // the lock time and pubkey. The V0 format of the script must be as follows: 500 // 501 // <lockTime> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 <pubkeyhash[20]> OP_EQUALVERIFY OP_CHECKSIG 502 // 503 // The script version refers to the pkScript version, not bond version, which 504 // pertains to DEX's version of the bond script. 505 func ExtractBondDetailsV0(scriptVersion uint16, bondScript []byte) (lockTime uint32, pkh []byte, err error) { 506 type templateMatch struct { 507 expectInt bool 508 maxIntBytes int 509 opcode byte 510 extractedInt int64 511 extractedData []byte 512 } 513 var template = [...]templateMatch{ 514 {expectInt: true, maxIntBytes: txscript.CltvMaxScriptNumLen}, // extractedInt 515 {opcode: txscript.OP_CHECKLOCKTIMEVERIFY}, 516 {opcode: txscript.OP_DROP}, 517 {opcode: txscript.OP_DUP}, 518 {opcode: txscript.OP_HASH160}, 519 {opcode: txscript.OP_DATA_20}, // extractedData 520 {opcode: txscript.OP_EQUALVERIFY}, 521 {opcode: txscript.OP_CHECKSIG}, 522 } 523 524 var templateOffset int 525 tokenizer := txscript.MakeScriptTokenizer(scriptVersion, bondScript) 526 for tokenizer.Next() { 527 if templateOffset >= len(template) { 528 return 0, nil, errors.New("too many script elements") 529 } 530 531 op, data := tokenizer.Opcode(), tokenizer.Data() 532 tplEntry := &template[templateOffset] 533 if tplEntry.expectInt { 534 switch { 535 case data != nil: 536 val, err := txscript.MakeScriptNum(data, tplEntry.maxIntBytes) 537 if err != nil { 538 return 0, nil, err 539 } 540 tplEntry.extractedInt = int64(val) 541 542 case txscript.IsSmallInt(op): // not expected for our lockTimes, but it is an integer 543 tplEntry.extractedInt = int64(txscript.AsSmallInt(op)) 544 545 default: 546 return 0, nil, errors.New("expected integer") 547 } 548 } else { 549 if op != tplEntry.opcode { 550 return 0, nil, fmt.Errorf("expected opcode %v, got %v", tplEntry.opcode, op) 551 } 552 553 tplEntry.extractedData = data 554 } 555 556 templateOffset++ 557 } 558 if err := tokenizer.Err(); err != nil { 559 return 0, nil, err 560 } 561 if !tokenizer.Done() || templateOffset != len(template) { 562 return 0, nil, errors.New("incorrect script length") 563 } 564 565 // The script matches in structure. Now validate the two pushes. 566 567 lockTime64 := template[0].extractedInt 568 if lockTime64 <= 0 || lockTime64 > MaxCLTVScriptNum { 569 return 0, nil, fmt.Errorf("invalid locktime %d", lockTime64) 570 } 571 lockTime = uint32(lockTime64) 572 573 const pubkeyHashLen = 20 574 bondPubKeyHash := template[5].extractedData 575 if len(bondPubKeyHash) != pubkeyHashLen { 576 err = errors.New("missing or invalid pubkeyhash data") 577 return 578 } 579 pkh = make([]byte, pubkeyHashLen) 580 copy(pkh, bondPubKeyHash) 581 582 return 583 } 584 585 // ExtractSwapDetails extacts the sender and receiver addresses from a swap 586 // contract. If the provided script is not a swap contract, an error will be 587 // returned. 588 func ExtractSwapDetails(pkScript []byte, chainParams *chaincfg.Params) ( 589 sender, receiver stdaddr.Address, lockTime uint64, secretHash []byte, err error) { 590 // A swap redemption sigScript is <pubkey> <secret> and satisfies the 591 // following swap contract, allowing only for a secret of size 592 // 593 // OP_IF 594 // OP_SIZE OP_DATA_1 secretSize OP_EQUALVERIFY OP_SHA256 OP_DATA_32 secretHash OP_EQUALVERIFY OP_DUP OP_HASH160 OP_DATA20 pkHashReceiver 595 // 1 + 1 + 1 + 1 + 1 + 1 + 32 + 1 + 1 + 1 + 1 + 20 596 // OP_ELSE 597 // OP_DATA4 lockTime OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 OP_DATA_20 pkHashSender 598 // 1 + 4 + 1 + 1 + 1 + 1 + 1 + 20 599 // OP_ENDIF 600 // OP_EQUALVERIFY 601 // OP_CHECKSIG 602 // 603 // 5 bytes if-else-endif-equalverify-checksig 604 // 1 + 1 + 1 + 1 + 1 + 1 + 32 + 1 + 1 + 1 + 1 + 20 = 62 bytes for redeem block 605 // 1 + 4 + 1 + 1 + 1 + 1 + 1 + 20 = 30 bytes for refund block 606 // 5 + 62 + 30 = 97 bytes 607 // 608 // Note that this allows for a secret size of up to 75 bytes, but the secret 609 // must be 32 bytes to be considered valid. 610 if len(pkScript) != SwapContractSize { 611 err = fmt.Errorf("incorrect swap contract length. expected %d, got %d", 612 SwapContractSize, len(pkScript)) 613 return 614 } 615 616 if pkScript[0] == txscript.OP_IF && 617 pkScript[1] == txscript.OP_SIZE && 618 pkScript[2] == txscript.OP_DATA_1 && 619 // secretSize (1 bytes) 620 pkScript[4] == txscript.OP_EQUALVERIFY && 621 pkScript[5] == txscript.OP_SHA256 && 622 pkScript[6] == txscript.OP_DATA_32 && 623 // secretHash (32 bytes) 624 pkScript[39] == txscript.OP_EQUALVERIFY && 625 pkScript[40] == txscript.OP_DUP && 626 pkScript[41] == txscript.OP_HASH160 && 627 pkScript[42] == txscript.OP_DATA_20 && 628 // receiver's pkh (20 bytes) 629 pkScript[63] == txscript.OP_ELSE && 630 pkScript[64] == txscript.OP_DATA_4 && 631 // lockTime (4 bytes) 632 pkScript[69] == txscript.OP_CHECKLOCKTIMEVERIFY && 633 pkScript[70] == txscript.OP_DROP && 634 pkScript[71] == txscript.OP_DUP && 635 pkScript[72] == txscript.OP_HASH160 && 636 pkScript[73] == txscript.OP_DATA_20 && 637 // sender's pkh (20 bytes) 638 pkScript[94] == txscript.OP_ENDIF && 639 pkScript[95] == txscript.OP_EQUALVERIFY && 640 pkScript[96] == txscript.OP_CHECKSIG { 641 642 if ssz := pkScript[3]; ssz != SecretKeySize { 643 return nil, nil, 0, nil, fmt.Errorf("invalid secret size %d", ssz) 644 } 645 646 receiver, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkScript[43:63], chainParams) 647 if err != nil { 648 return nil, nil, 0, nil, fmt.Errorf("error decoding address from recipient's pubkey hash") 649 } 650 651 sender, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkScript[74:94], chainParams) 652 if err != nil { 653 return nil, nil, 0, nil, fmt.Errorf("error decoding address from sender's pubkey hash") 654 } 655 656 lockTime = uint64(binary.LittleEndian.Uint32(pkScript[65:69])) 657 secretHash = pkScript[7:39] 658 659 return 660 } 661 662 err = fmt.Errorf("invalid swap contract") 663 return 664 } 665 666 // IsDust returns whether or not the passed transaction output amount is 667 // considered dust or not based on the passed minimum transaction relay fee. 668 // Dust is defined in terms of the minimum transaction relay fee. 669 // See dcrd/mempool/policy isDust for further documentation, though this version 670 // accepts atoms/byte rather than atoms/kB. 671 func IsDust(txOut *wire.TxOut, minRelayTxFee uint64) bool { 672 // Unspendable outputs are considered dust. 673 if txscript.IsUnspendable(txOut.Value, txOut.PkScript) { 674 return true 675 } 676 return IsDustVal(uint64(txOut.SerializeSize()), uint64(txOut.Value), minRelayTxFee) 677 } 678 679 // IsDustVal is like IsDust but it only needs the size of the serialized output 680 // and its amount. 681 func IsDustVal(txOutSize, amt, minRelayTxFee uint64) bool { 682 totalSize := txOutSize + 165 683 return amt/(3*totalSize) < minRelayTxFee 684 } 685 686 // ExtractScriptHash extracts the script hash from a P2SH pkScript of a 687 // particular script version. 688 func ExtractScriptHash(version uint16, script []byte) []byte { 689 switch version { 690 case 0: 691 return ExtractScriptHashV0(script) 692 } 693 return nil 694 } 695 696 // ExtractScriptHashV0 extracts the script hash from a P2SH pkScript. This is 697 // valid only for version 0 scripts. 698 func ExtractScriptHashV0(script []byte) []byte { 699 if h := stdscript.ExtractScriptHashV0(script); h != nil { 700 return h 701 } 702 return stdscript.ExtractStakeScriptHashV0(script) 703 } 704 705 // ExtractScriptData extracts script type, addresses, and required signature 706 // count from a pkScript. Non-standard scripts are not an error; non-nil errors 707 // are only returned if the script cannot be parsed. See also InputInfo for 708 // additional signature script size data 709 func ExtractScriptData(version uint16, script []byte, chainParams *chaincfg.Params) (ScriptType, []string, int) { 710 class, addrs := stdscript.ExtractAddrs(version, script, chainParams) 711 if class == stdscript.STNonStandard { 712 return ScriptUnsupported, nil, 0 713 } 714 numRequired := stdscript.DetermineRequiredSigs(version, script) 715 scriptType := convertScriptType(class) 716 addresses := make([]string, len(addrs)) 717 for i, addr := range addrs { 718 addresses[i] = addr.String() 719 } 720 721 return scriptType, addresses, int(numRequired) 722 } 723 724 // ScriptAddrs is information about the pubkeys or pubkey hashes present in a 725 // scriptPubKey (and the redeem script, for p2sh). This information can be used 726 // to estimate the spend script size, e.g. pubkeys in a redeem script don't 727 // require pubkeys in the scriptSig, but pubkey hashes do. 728 type ScriptAddrs struct { 729 PubKeys []stdaddr.Address 730 PkHashes []stdaddr.Address 731 NRequired int 732 } 733 734 // ExtractScriptAddrs extracts the addresses from script. Addresses are 735 // separated into pubkey and pubkey hash, where the pkh addresses are actually a 736 // catch all for non-P2PK addresses. As such, this function is not intended for 737 // use on P2SH pkScripts. Rather, the corresponding redeem script should be 738 // processed with ExtractScriptAddrs. The returned bool indicates if the script 739 // is non-standard. 740 func ExtractScriptAddrs(version uint16, script []byte, chainParams *chaincfg.Params) (ScriptType, *ScriptAddrs) { 741 pubkeys := make([]stdaddr.Address, 0) 742 pkHashes := make([]stdaddr.Address, 0) 743 class, addrs := stdscript.ExtractAddrs(version, script, chainParams) 744 if class == stdscript.STNonStandard { 745 return ScriptUnsupported, &ScriptAddrs{} 746 } 747 for _, addr := range addrs { 748 // If the address is an unhashed public key, is won't need a pubkey as 749 // part of its sigScript, so count them separately. 750 _, isPubkey := addr.(stdaddr.SerializedPubKeyer) 751 if isPubkey { 752 pubkeys = append(pubkeys, addr) 753 } else { 754 pkHashes = append(pkHashes, addr) 755 } 756 } 757 numRequired := stdscript.DetermineRequiredSigs(version, script) 758 scriptType := convertScriptType(class) 759 return scriptType, &ScriptAddrs{ 760 PubKeys: pubkeys, 761 PkHashes: pkHashes, 762 NRequired: int(numRequired), 763 } 764 } 765 766 // SpendInfo is information about an input and it's previous outpoint. 767 type SpendInfo struct { 768 SigScriptSize uint32 769 ScriptAddrs *ScriptAddrs 770 ScriptType ScriptType 771 NonStandardScript bool // refers to redeem script for P2SH ScriptType 772 } 773 774 // Size is the serialized size of the input. 775 func (nfo *SpendInfo) Size() uint32 { 776 return TxInOverhead + uint32(wire.VarIntSerializeSize(uint64(nfo.SigScriptSize))) + nfo.SigScriptSize 777 } 778 779 // DataPrefixSize returns the size of the size opcodes that would precede the 780 // data in a script. Certain data of length 0 and 1 is represented with OP_# or 781 // OP_1NEGATE codes directly without preceding OP_DATA_# OR OP_PUSHDATA# codes. 782 func DataPrefixSize(data []byte) uint8 { 783 serSz := txscript.CanonicalDataSize(data) 784 sz := len(data) 785 prefixSize := serSz - sz 786 if sz == 0 { 787 prefixSize-- // empty array uses a byte 788 } 789 return uint8(prefixSize) 790 } 791 792 // InputInfo is some basic information about the input required to spend an 793 // output. Only P2PKH and P2SH pkScripts are supported. If the pubkey script 794 // parses as P2SH, the redeem script must be provided. 795 func InputInfo(version uint16, pkScript, redeemScript []byte, chainParams *chaincfg.Params) (*SpendInfo, error) { 796 scriptType, scriptAddrs := ExtractScriptAddrs(version, pkScript, chainParams) 797 switch { 798 case scriptType == ScriptUnsupported: 799 return nil, dex.UnsupportedScriptError 800 case scriptType.IsP2PK(): 801 return &SpendInfo{ 802 SigScriptSize: P2PKSigScriptSize, 803 ScriptAddrs: scriptAddrs, 804 ScriptType: scriptType, 805 }, nil 806 case scriptType.IsP2PKH(): 807 return &SpendInfo{ 808 SigScriptSize: P2PKHSigScriptSize, 809 ScriptAddrs: scriptAddrs, 810 ScriptType: scriptType, 811 }, nil 812 case scriptType.IsP2SH(): 813 default: 814 return nil, fmt.Errorf("unsupported pkScript: %d", scriptType) // dex.UnsupportedScriptError too? 815 } 816 817 // P2SH 818 if len(redeemScript) == 0 { 819 return nil, fmt.Errorf("no redeem script provided for P2SH pubkey script") 820 } 821 822 var redeemScriptType ScriptType 823 redeemScriptType, scriptAddrs = ExtractScriptAddrs(version, redeemScript, chainParams) 824 if redeemScriptType == ScriptUnsupported { 825 return &SpendInfo{ 826 // SigScriptSize cannot be determined, leave zero. 827 ScriptAddrs: scriptAddrs, 828 ScriptType: scriptType, // still P2SH 829 NonStandardScript: true, // but non-standard redeem script (e.g. contract) 830 }, nil 831 } 832 833 // NOTE: scriptAddrs are now from the redeemScript, and we add the multisig 834 // bit to scriptType. 835 if redeemScriptType.IsMultiSig() { 836 scriptType |= ScriptMultiSig 837 } 838 839 // If it's a P2SH, with a standard redeem script, compute the sigScript size 840 // for the expected number of signatures, pubkeys, and the redeem script 841 // itself. Start with the signatures. 842 sigScriptSize := (1 + DERSigLength) * scriptAddrs.NRequired // 73 max for sig, 1 for push code 843 // If there are pubkey-hash addresses, they'll need pubkeys. 844 if len(scriptAddrs.PkHashes) > 0 { 845 sigScriptSize += scriptAddrs.NRequired * (pubkeyLength + 1) 846 } 847 // Then add the length of the script and the push opcode byte(s). 848 sigScriptSize += len(redeemScript) + int(DataPrefixSize(redeemScript)) // push data length op code might be >1 byte 849 return &SpendInfo{ 850 SigScriptSize: uint32(sigScriptSize), 851 ScriptAddrs: scriptAddrs, 852 ScriptType: scriptType, 853 }, nil 854 } 855 856 // IsRefundScript checks if the signature script is of the expected format for 857 // the standard swap contract refund. The signature and pubkey data pushes are 858 // not validated other than ensuring they are data pushes. The provided contract 859 // must correspond to the final data push in the sigScript, but it is otherwise 860 // not validated either. 861 func IsRefundScript(scriptVersion uint16, sigScript, contract []byte) bool { 862 tokenizer := txscript.MakeScriptTokenizer(scriptVersion, sigScript) 863 // sig 864 if !tokenizer.Next() { 865 return false 866 } 867 if tokenizer.Data() == nil { 868 return false // should be a der signature 869 } 870 871 // pubkey 872 if !tokenizer.Next() { 873 return false 874 } 875 if tokenizer.Data() == nil { 876 return false // should be a pubkey 877 } 878 879 // OP_0 880 if !tokenizer.Next() { 881 return false 882 } 883 if tokenizer.Opcode() != txscript.OP_0 { 884 return false 885 } 886 887 // contract script 888 if !tokenizer.Next() { 889 return false 890 } 891 push := tokenizer.Data() 892 if len(push) != SwapContractSize { 893 return false 894 } 895 if !bytes.Equal(push, contract) { 896 return false 897 } 898 899 return tokenizer.Done() 900 } 901 902 // FindKeyPush attempts to extract the secret key from the signature script. The 903 // contract must be provided for the search algorithm to verify the correct data 904 // push. Only contracts of length SwapContractSize that can be validated by 905 // ExtractSwapDetails are recognized. 906 func FindKeyPush(scriptVersion uint16, sigScript, contractHash []byte, chainParams *chaincfg.Params) ([]byte, error) { 907 tokenizer := txscript.MakeScriptTokenizer(scriptVersion, sigScript) 908 909 // The contract is pushed after the key, find the contract starting with the 910 // first data push and record all data pushes encountered before the contract 911 // push. One of those preceding pushes should be the key push. 912 var dataPushesUpTillContract [][]byte 913 var keyHash []byte 914 var err error 915 for tokenizer.Next() { 916 push := tokenizer.Data() 917 918 // Only hash if ExtractSwapDetails will recognize it. 919 if len(push) == SwapContractSize { 920 h := dcrutil.Hash160(push) 921 if bytes.Equal(h, contractHash) { 922 _, _, _, keyHash, err = ExtractSwapDetails(push, chainParams) 923 if err != nil { 924 return nil, fmt.Errorf("error extracting atomic swap details: %w", err) 925 } 926 break // contract is pushed after the key, if we've encountered the contract, we must have just passed the key 927 } 928 } 929 930 // Save this push as preceding the contract push. 931 if push != nil { 932 dataPushesUpTillContract = append(dataPushesUpTillContract, push) 933 } 934 } 935 if tokenizer.Err() != nil { 936 return nil, tokenizer.Err() 937 } 938 939 if len(keyHash) > 0 { 940 // The key push should be the data push immediately preceding the contract 941 // push, but iterate through all of the preceding pushes backwards to ensure 942 // it is not hidden behind some non-standard script. 943 for i := len(dataPushesUpTillContract) - 1; i >= 0; i-- { 944 push := dataPushesUpTillContract[i] 945 946 // We have the key hash from the contract. See if this is the key. 947 h := sha256.Sum256(push) 948 if bytes.Equal(h[:], keyHash) { 949 return push, nil 950 } 951 } 952 } 953 954 return nil, fmt.Errorf("key not found") 955 }