package txscript import ( "fmt" "github.com/p9c/p9/pkg/btcaddr" "github.com/p9c/p9/pkg/chaincfg" ) // ScriptClass is an enumeration for the list of standard types of script. type ScriptClass byte // A bunch of constants const ( // MaxDataCarrierSize is the maximum number of bytes allowed in pushed data to // be considered a nulldata transaction MaxDataCarrierSize = 80 // StandardVerifyFlags are the script flags which are used when executing // transaction scripts to enforce additional checks which are required for the // script to be considered standard. These checks help reduce issues related to // transaction malleability as well as allow pay-to-script hash transactions. // Note these flags are different than what is required for the consensus rules // in that they are more strict. // TODO: This definition does not belong here. It belongs in a policy package. StandardVerifyFlags = ScriptBip16 | ScriptVerifyDERSignatures | ScriptVerifyStrictEncoding | ScriptVerifyMinimalData | ScriptStrictMultiSig | ScriptDiscourageUpgradableNops | ScriptVerifyCleanStack | ScriptVerifyNullFail | ScriptVerifyCheckLockTimeVerify | // ScriptVerifyCheckSequenceVerify | ScriptVerifyLowS | ScriptStrictMultiSig | // ScriptVerifyWitness | // ScriptVerifyDiscourageUpgradeableWitnessProgram | ScriptVerifyMinimalIf // ScriptVerifyWitnessPubKeyType // Classes of script payment known about in the blockchain. NonStandardTy ScriptClass = iota // None of the recognized forms. PubKeyTy // Pay pubkey. PubKeyHashTy // Pay pubkey hash. // WitnessV0PubKeyHashTy // Pay witness pubkey hash. ScriptHashTy // Pay to script hash. // WitnessV0ScriptHashTy // Pay to witness script hash. MultiSigTy // Multi signature. NullDataTy // Empty data-only (provably prunable). ) // scriptClassToName houses the human-readable strings which describe each // script class. var scriptClassToName = []string{ NonStandardTy: "nonstandard", PubKeyTy: "pubkey", PubKeyHashTy: "pubkeyhash", // WitnessV0PubKeyHashTy: "witness_v0_keyhash", ScriptHashTy: "scripthash", // WitnessV0ScriptHashTy: "witness_v0_scripthash", MultiSigTy: "multisig", NullDataTy: "nulldata", } // String implements the Stringer interface by returning the name of the enum // script class. If the enum is invalid then "Invalid" will be returned. func (t ScriptClass) String() string { if int(t) > len(scriptClassToName) || int(t) < 0 { return "Invalid" } return scriptClassToName[t] } // isPubkey returns true if the script passed is a pay-to-pubkey transaction, // false otherwise. func isPubkey(pops []parsedOpcode) bool { // Valid pubkeys are either 33 or 65 bytes. return len(pops) == 2 && (len(pops[0].data) == 33 || len(pops[0].data) == 65) && pops[1].opcode.value == OP_CHECKSIG } // isPubkeyHash returns true if the script passed is a pay-to-pubkey-hash // transaction, false otherwise. func isPubkeyHash(pops []parsedOpcode) bool { return len(pops) == 5 && pops[0].opcode.value == OP_DUP && pops[1].opcode.value == OP_HASH160 && pops[2].opcode.value == OP_DATA_20 && pops[3].opcode.value == OP_EQUALVERIFY && pops[4].opcode.value == OP_CHECKSIG } // isMultiSig returns true if the passed script is a multisig transaction, false // otherwise. func isMultiSig(pops []parsedOpcode) bool { // The absolute minimum is 1 pubkey: // OP_0/OP_1-16 OP_1 OP_CHECKMULTISIG l := len(pops) if l < 4 { return false } if !isSmallInt(pops[0].opcode) { return false } if !isSmallInt(pops[l-2].opcode) { return false } if pops[l-1].opcode.value != OP_CHECKMULTISIG { return false } // Verify the number of pubkeys specified matches the actual number of pubkeys provided. if l-2-1 != asSmallInt(pops[l-2].opcode) { return false } for _, pop := range pops[1 : l-2] { // Valid pubkeys are either 33 or 65 bytes. if len(pop.data) != 33 && len(pop.data) != 65 { return false } } return true } // isNullData returns true if the passed script is a null data transaction, // false otherwise. func isNullData(pops []parsedOpcode) bool { // A nulldata transaction is either a single OP_RETURN or an OP_RETURN SMALLDATA // (where SMALLDATA is a data push up to MaxDataCarrierSize bytes). l := len(pops) if l == 1 && pops[0].opcode.value == OP_RETURN { return true } return l == 2 && pops[0].opcode.value == OP_RETURN && (isSmallInt(pops[1].opcode) || pops[1].opcode.value <= OP_PUSHDATA4) && len(pops[1].data) <= MaxDataCarrierSize } // scriptType returns the type of the script being inspected from the known // standard types. func typeOfScript(pops []parsedOpcode) ScriptClass { if isPubkey(pops) { return PubKeyTy } else if isPubkeyHash(pops) { return PubKeyHashTy // } else if isWitnessPubKeyHash(pops) { // return WitnessV0PubKeyHashTy } else if isScriptHash(pops) { return ScriptHashTy // } else if isWitnessScriptHash(pops) { // return WitnessV0ScriptHashTy } else if isMultiSig(pops) { return MultiSigTy } else if isNullData(pops) { return NullDataTy } return NonStandardTy } // GetScriptClass returns the class of the script passed. NonStandardTy will be // returned when the script does not parse. func GetScriptClass(script []byte) ScriptClass { pops, e := parseScript(script) if e != nil { return NonStandardTy } return typeOfScript(pops) } // expectedInputs returns the number of arguments required by a script. If the // script is of unknown type such that the number can not be determined then -1 // is returned. We are an internal function and thus assume that class is the // real class of pops (and we can thus assume things that were determined while // finding out the type). func expectedInputs(pops []parsedOpcode, class ScriptClass) int { switch class { case PubKeyTy: return 1 case PubKeyHashTy: return 2 // case WitnessV0PubKeyHashTy: // return 2 case ScriptHashTy: // Not including script. That is handled by the caller. return 1 // case WitnessV0ScriptHashTy: // // Not including script. That is handled by the caller. // return 1 case MultiSigTy: // Standard multisig has a push a small number for the number of sigs and number // of keys. Chk the first push instruction to see how many arguments are // expected. typeOfScript already checked this so we know it'll be a small int. // Also, due to the original bitcoind bug where OP_CHECKMULTISIG pops an // additional item from the stack, add an extra expected input for the extra // push that is required to compensate. return asSmallInt(pops[0].opcode) + 1 case NullDataTy: fallthrough default: return -1 } } // ScriptInfo houses information about a script pair that is determined by // CalcScriptInfo. type ScriptInfo struct { // PkScriptClass is the class of the public key script and is equivalent to // calling GetScriptClass on it. PkScriptClass ScriptClass // NumInputs is the number of inputs provided by the public key script. NumInputs int // ExpectedInputs is the number of outputs required by the signature script and // any pay-to-script-hash scripts. The number will be -1 if unknown. ExpectedInputs int // SigOps is the number of signature operations in the script pair. SigOps int } // CalcScriptInfo returns a structure providing data about the provided script // pair. It will error if the pair is in someway invalid such that they can not // be analysed, i.e. if they do not parse or the pkScript is not a push-only // script func CalcScriptInfo(sigScript, pkScript []byte, bip16 bool) (si *ScriptInfo, e error) { var sigPops []parsedOpcode if sigPops, e = parseScript(sigScript); E.Chk(e) { return } var pkPops []parsedOpcode if pkPops, e = parseScript(pkScript); E.Chk(e) { return } // Push only sigScript makes little sense. si = new(ScriptInfo) si.PkScriptClass = typeOfScript(pkPops) // Can't have a signature script that doesn't just push data. if !isPushOnly(sigPops) { return nil, scriptError( ErrNotPushOnly, "signature script is not push only", ) } si.ExpectedInputs = expectedInputs(pkPops, si.PkScriptClass) switch { // Count sigops taking into account pay-to-script-hash. case si.PkScriptClass == ScriptHashTy && bip16: // The pay-to-hash-script is the final data push of the signature script. script := sigPops[len(sigPops)-1].data shPops, e := parseScript(script) if e != nil { return nil, e } shInputs := expectedInputs(shPops, typeOfScript(shPops)) if shInputs == -1 { si.ExpectedInputs = -1 } else { si.ExpectedInputs += shInputs } si.SigOps = getSigOpCount(shPops, true) // All entries pushed to stack (or are OP_RESERVED and exec will fail). si.NumInputs = len(sigPops) // // If segwit is active, and this is a regular p2wkh output, then we'll treat the script as a p2pkh output in // // essence. // case si.PkScriptClass == WitnessV0PubKeyHashTy && segwit: // si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness) // si.NumInputs = len(witness) // // We'll attempt to detect the nested p2sh case so we can accurately count the signature operations involved. // case si.PkScriptClass == ScriptHashTy && // IsWitnessProgram(sigScript[1:]) && bip16 && segwit: // // Extract the pushed witness program from the sigScript so we can determine the // // number of expected inputs. // pkPops, _ := parseScript(sigScript[1:]) // shInputs := expectedInputs(pkPops, typeOfScript(pkPops)) // if shInputs == -1 { // si.ExpectedInputs = -1 // } else { // si.ExpectedInputs += shInputs // } // si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness) // si.NumInputs = len(witness) // si.NumInputs += len(sigPops) // // If segwit is active, and this is a p2wsh output, then we'll need to examine // // the witness script to generate accurate script info. // case si.PkScriptClass == WitnessV0ScriptHashTy && segwit: // // The witness script is the final element of the witness stack. // witnessScript := witness[len(witness)-1] // pops, _ := parseScript(witnessScript) // shInputs := expectedInputs(pops, typeOfScript(pops)) // if shInputs == -1 { // si.ExpectedInputs = -1 // } else { // si.ExpectedInputs += shInputs // } // si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness) // si.NumInputs = len(witness) default: si.SigOps = getSigOpCount(pkPops, true) // All entries pushed to stack (or are OP_RESERVED and exec will fail). si.NumInputs = len(sigPops) } return si, nil } // CalcMultiSigStats returns the number of public keys and signatures from a // multi-signature transaction script. The passed script MUST already be known // to be a multi-signature script. func CalcMultiSigStats(script []byte) (int, int, error) { pops, e := parseScript(script) if e != nil { return 0, 0, e } // A multi-signature script is of the pattern: // // NUM_SIGS PUBKEY PUBKEY PUBKEY... NUM_PUBKEYS OP_CHECKMULTISIG // // Therefore the number of signatures is the oldest item on the stack and the // number of pubkeys is the 2nd to last. Also, the absolute minimum for a // multi-signature script is 1 pubkey, so at least 4 items must be on the stack // per: // // OP_1 PUBKEY OP_1 OP_CHECKMULTISIG if len(pops) < 4 { str := fmt.Sprintf("script %x is not a multisig script", script) return 0, 0, scriptError(ErrNotMultisigScript, str) } numSigs := asSmallInt(pops[0].opcode) numPubKeys := asSmallInt(pops[len(pops)-2].opcode) return numPubKeys, numSigs, nil } // payToPubKeyHashScript creates a new script to pay a transaction output to a // 20-byte pubkey hash. It is expected that the input is a valid hash. func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160). AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddOp(OP_CHECKSIG). Script() } // // payToWitnessPubKeyHashScript creates a new script to pay to a version 0 // // pubkey hash witness program. The passed hash is expected to be valid. // func payToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { // return NewScriptBuilder().AddOp(OP_0).AddData(pubKeyHash).Script() // } // payToScriptHashScript creates a new script to pay a transaction output to a // script hash. It is expected that the input is a valid hash. func payToScriptHashScript(scriptHash []byte) ([]byte, error) { return NewScriptBuilder().AddOp(OP_HASH160).AddData(scriptHash). AddOp(OP_EQUAL).Script() } // payToWitnessPubKeyHashScript creates a new script to pay to a version 0 // script hash witness program. The passed hash is expected to be valid. func payToWitnessScriptHashScript(scriptHash []byte) ([]byte, error) { return NewScriptBuilder().AddOp(OP_0).AddData(scriptHash).Script() } // payToPubkeyScript creates a new script to pay a transaction output to a // public key. It is expected that the input is a valid pubkey. func payToPubKeyScript(serializedPubKey []byte) ([]byte, error) { return NewScriptBuilder().AddData(serializedPubKey). AddOp(OP_CHECKSIG).Script() } // PayToAddrScript creates a new script to pay a transaction output to a the specified address. func PayToAddrScript(addr btcaddr.Address) ([]byte, error) { const nilAddrErrStr = "unable to generate payment script for nil address" switch addr := addr.(type) { case *btcaddr.PubKeyHash: if addr == nil { return nil, scriptError( ErrUnsupportedAddress, nilAddrErrStr, ) } return payToPubKeyHashScript(addr.ScriptAddress()) case *btcaddr.ScriptHash: if addr == nil { return nil, scriptError( ErrUnsupportedAddress, nilAddrErrStr, ) } return payToScriptHashScript(addr.ScriptAddress()) case *btcaddr.PubKey: if addr == nil { return nil, scriptError( ErrUnsupportedAddress, nilAddrErrStr, ) } return payToPubKeyScript(addr.ScriptAddress()) // case *util.AddressWitnessPubKeyHash: // if addr == nil { // return nil, scriptError(ErrUnsupportedAddress, // nilAddrErrStr) // } // return payToWitnessPubKeyHashScript(addr.ScriptAddress()) // case *util.AddressWitnessScriptHash: // if addr == nil { // return nil, scriptError(ErrUnsupportedAddress, // nilAddrErrStr) // } // return payToWitnessScriptHashScript(addr.ScriptAddress()) } str := fmt.Sprintf( "unable to generate payment script for unsupported "+ "address type %T", addr, ) return nil, scriptError(ErrUnsupportedAddress, str) } // NullDataScript creates a provably-prunable script containing OP_RETURN // followed by the passed data. An ScriptError with the error code // errTooMuchNullData will be returned if the length of the passed data exceeds // MaxDataCarrierSize. func NullDataScript(data []byte) ([]byte, error) { if len(data) > MaxDataCarrierSize { str := fmt.Sprintf( "data size %d is larger than max "+ "allowed size %d", len(data), MaxDataCarrierSize, ) return nil, scriptError(ErrTooMuchNullData, str) } return NewScriptBuilder().AddOp(OP_RETURN).AddData(data).Script() } // MultiSigScript returns a valid script for a multisignature redemption where // nrequired of the keys in pubkeys are required to have signed the transaction // for success. An ScriptError with the error code errTooManyRequiredSigs will // be returned if nrequired is larger than the number of keys provided. func MultiSigScript(pubkeys []*btcaddr.PubKey, nrequired int) ([]byte, error) { if len(pubkeys) < nrequired { str := fmt.Sprintf( "unable to generate multisig script with "+ "%d required signatures when there are only %d public "+ "keys available", nrequired, len(pubkeys), ) return nil, scriptError(ErrTooManyRequiredSigs, str) } builder := NewScriptBuilder().AddInt64(int64(nrequired)) for _, key := range pubkeys { builder.AddData(key.ScriptAddress()) } builder.AddInt64(int64(len(pubkeys))) builder.AddOp(OP_CHECKMULTISIG) return builder.Script() } // PushedData returns an array of byte slices containing any pushed data found // in the passed script. This includes OP_0, but not OP_1 - OP_16. func PushedData(script []byte) ([][]byte, error) { pops, e := parseScript(script) if e != nil { return nil, e } var data [][]byte for _, pop := range pops { if pop.data != nil { data = append(data, pop.data) } else if pop.opcode.value == OP_0 { data = append(data, nil) } } return data, nil } // ExtractPkScriptAddrs returns the type of script, addresses and required // signatures associated with the passed PkScript. Note that it only works for // 'standard' transaction script types. Any data such as public keys which are // invalid are omitted from the results. func ExtractPkScriptAddrs(pkScript []byte, chainParams *chaincfg.Params) (ScriptClass, []btcaddr.Address, int, error) { var addrs []btcaddr.Address var requiredSigs int // No valid addresses or required signatures if the script doesn't parse. pops, e := parseScript(pkScript) if e != nil { return NonStandardTy, nil, 0, e } scriptClass := typeOfScript(pops) switch scriptClass { case PubKeyHashTy: // A pay-to-pubkey-hash script is of the form: OP_DUP OP_HASH160 // OP_EQUALVERIFY OP_CHECKSIG Therefore the pubkey hash is the 3rd item on the // stack. Skip the pubkey hash if it's invalid for some reason. requiredSigs = 1 addr, e := btcaddr.NewPubKeyHash( pops[2].data, chainParams, ) if e == nil { addrs = append(addrs, addr) } // case WitnessV0PubKeyHashTy: // // A pay-to-witness-pubkey-hash script is of the form: OP_0 <20-byte hash> // // Therefore, the pubkey hash is the second item on the stack. Skip the pubkey // // hash if it's invalid for some reason. // requiredSigs = 1 // addr, e := util.NewAddressWitnessPubKeyHash(pops[1].data, // chainParams) // if e == nil { // addrs = append(addrs, addr) // } case PubKeyTy: // A pay-to-pubkey script is of the form: OP_CHECKSIG Therefore the pubkey is the first item on the // stack. Skip the pubkey if it's invalid for some reason. requiredSigs = 1 addr, e := btcaddr.NewPubKey(pops[0].data, chainParams) if e == nil { addrs = append(addrs, addr) } case ScriptHashTy: // A pay-to-script-hash script is of the form: OP_HASH160 OP_EQUAL Therefore the script hash is the // 2nd item on the stack. Skip the script hash if it's invalid for some reason. requiredSigs = 1 addr, e := btcaddr.NewScriptHashFromHash( pops[1].data, chainParams, ) if e == nil { addrs = append(addrs, addr) } // case WitnessV0ScriptHashTy: // // A pay-to-witness-script-hash script is of the form: OP_0 <32-byte hash> // // Therefore, the script hash is the second item on the stack. Skip the script // // hash if it's invalid for some reason. // requiredSigs = 1 // addr, e := util.NewAddressWitnessScriptHash(pops[1].data, // chainParams) // if e == nil { // addrs = append(addrs, addr) // } case MultiSigTy: // A multi-signature script is of the form: // ... OP_CHECKMULTISIG Therefore the number of required // signatures is the 1st item on the stack and the number of public keys is the // 2nd to last item on the stack. requiredSigs = asSmallInt(pops[0].opcode) numPubKeys := asSmallInt(pops[len(pops)-2].opcode) // Extract the public keys while skipping any that are invalid. addrs = make([]btcaddr.Address, 0, numPubKeys) for i := 0; i < numPubKeys; i++ { addr, e := btcaddr.NewPubKey( pops[i+1].data, chainParams, ) if e == nil { addrs = append(addrs, addr) } } case NullDataTy: // Null data transactions have no addresses or required signatures. case NonStandardTy: // Don't attempt to extract addresses or required signatures for nonstandard // transactions. } return scriptClass, addrs, requiredSigs, nil } // AtomicSwapDataPushes houses the data pushes found in atomic swap contracts. type AtomicSwapDataPushes struct { RecipientHash160 [20]byte RefundHash160 [20]byte SecretHash [32]byte SecretSize int64 LockTime int64 } // ExtractAtomicSwapDataPushes returns the data pushes from an atomic swap // contract. If the script is not an atomic swap contract, // // ExtractAtomicSwapDataPushes returns (nil, nil). Non-nil errors are returned // for unparsable scripts. NOTE: Atomic swaps are not considered standard script // types by the dcrd mempool policy and should be used with P2SH. The atomic // swap format is also expected to change to use a more secure hash function in // the future. This function is only defined in the txscript package due to API // limitations which prevent callers using txscript to parse nonstandard // scripts. func ExtractAtomicSwapDataPushes(version uint16, pkScript []byte) (*AtomicSwapDataPushes, error) { pops, e := parseScript(pkScript) if e != nil { return nil, e } if len(pops) != 20 { return nil, nil } isAtomicSwap := pops[0].opcode.value == OP_IF && pops[1].opcode.value == OP_SIZE && canonicalPush(pops[2]) && pops[3].opcode.value == OP_EQUALVERIFY && pops[4].opcode.value == OP_SHA256 && pops[5].opcode.value == OP_DATA_32 && pops[6].opcode.value == OP_EQUALVERIFY && pops[7].opcode.value == OP_DUP && pops[8].opcode.value == OP_HASH160 && pops[9].opcode.value == OP_DATA_20 && pops[10].opcode.value == OP_ELSE && canonicalPush(pops[11]) && pops[12].opcode.value == OP_CHECKLOCKTIMEVERIFY && pops[13].opcode.value == OP_DROP && pops[14].opcode.value == OP_DUP && pops[15].opcode.value == OP_HASH160 && pops[16].opcode.value == OP_DATA_20 && pops[17].opcode.value == OP_ENDIF && pops[18].opcode.value == OP_EQUALVERIFY && pops[19].opcode.value == OP_CHECKSIG if !isAtomicSwap { return nil, nil } pushes := new(AtomicSwapDataPushes) copy(pushes.SecretHash[:], pops[5].data) copy(pushes.RecipientHash160[:], pops[9].data) copy(pushes.RefundHash160[:], pops[16].data) if pops[2].data != nil { locktime, e := makeScriptNum(pops[2].data, true, 5) if e != nil { return nil, nil } pushes.SecretSize = int64(locktime) } else if op := pops[2].opcode; isSmallInt(op) { pushes.SecretSize = int64(asSmallInt(op)) } else { return nil, nil } if pops[11].data != nil { locktime, e := makeScriptNum(pops[11].data, true, 5) if e != nil { return nil, nil } pushes.LockTime = int64(locktime) } else if op := pops[11].opcode; isSmallInt(op) { pushes.LockTime = int64(asSmallInt(op)) } else { return nil, nil } return pushes, nil }