github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/vm/vmutil/script.go (about)

     1  package vmutil
     2  
     3  import (
     4  	"crypto/ed25519"
     5  
     6  	"github.com/bytom/bytom/consensus/bcrp"
     7  	"github.com/bytom/bytom/errors"
     8  	"github.com/bytom/bytom/protocol/vm"
     9  )
    10  
    11  // pre-define errors
    12  var (
    13  	ErrBadValue       = errors.New("bad value")
    14  	ErrMultisigFormat = errors.New("bad multisig program format")
    15  )
    16  
    17  // IsUnspendable checks if a contorl program is absolute failed
    18  func IsUnspendable(prog []byte) bool {
    19  	return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
    20  }
    21  
    22  func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
    23  	if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
    24  		return err
    25  	}
    26  
    27  	b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
    28  	for _, p := range pubkeys {
    29  		b.AddData(p)
    30  	}
    31  	b.AddUint64(uint64(nrequired))    // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
    32  	b.AddUint64(uint64(len(pubkeys))) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
    33  	b.AddOp(vm.OP_CHECKMULTISIG)      // stack is now [... NARGS]
    34  	return nil
    35  }
    36  
    37  // DefaultCoinbaseProgram generates the script for contorl coinbase output
    38  func DefaultCoinbaseProgram() ([]byte, error) {
    39  	builder := NewBuilder()
    40  	builder.AddOp(vm.OP_TRUE)
    41  	return builder.Build()
    42  }
    43  
    44  // P2WPKHProgram return the segwit pay to public key hash
    45  func P2WPKHProgram(hash []byte) ([]byte, error) {
    46  	builder := NewBuilder()
    47  	builder.AddUint64(0)
    48  	builder.AddData(hash)
    49  	return builder.Build()
    50  }
    51  
    52  // P2WSHProgram return the segwit pay to script hash
    53  func P2WSHProgram(hash []byte) ([]byte, error) {
    54  	builder := NewBuilder()
    55  	builder.AddUint64(0)
    56  	builder.AddData(hash)
    57  	return builder.Build()
    58  }
    59  
    60  // RetireProgram generates the script for retire output
    61  func RetireProgram(comment []byte) ([]byte, error) {
    62  	builder := NewBuilder()
    63  	builder.AddOp(vm.OP_FAIL)
    64  	if len(comment) != 0 {
    65  		builder.AddData(comment)
    66  	}
    67  	return builder.Build()
    68  }
    69  
    70  // RegisterProgram generates the script for register contract output
    71  // follow BCRP(bytom contract register protocol)
    72  func RegisterProgram(contract []byte) ([]byte, error) {
    73  	builder := NewBuilder()
    74  	builder.AddOp(vm.OP_FAIL)
    75  	builder.AddData([]byte(bcrp.BCRP))
    76  	builder.AddData([]byte{byte(bcrp.Version)})
    77  	builder.AddData(contract)
    78  	return builder.Build()
    79  }
    80  
    81  // CallContractProgram generates the script for control contract output
    82  // follow BCRP(bytom contract register protocol)
    83  func CallContractProgram(hash []byte) ([]byte, error) {
    84  	builder := NewBuilder()
    85  	builder.AddData([]byte(bcrp.BCRP))
    86  	builder.AddData(hash)
    87  	return builder.Build()
    88  }
    89  
    90  // P2PKHSigProgram generates the script for control with pubkey hash
    91  func P2PKHSigProgram(pubkeyHash []byte) ([]byte, error) {
    92  	builder := NewBuilder()
    93  	builder.AddOp(vm.OP_DUP)
    94  	builder.AddOp(vm.OP_HASH160)
    95  	builder.AddData(pubkeyHash)
    96  	builder.AddOp(vm.OP_EQUALVERIFY)
    97  	builder.AddOp(vm.OP_TXSIGHASH)
    98  	builder.AddOp(vm.OP_SWAP)
    99  	builder.AddOp(vm.OP_CHECKSIG)
   100  	return builder.Build()
   101  }
   102  
   103  // P2SHProgram generates the script for control with script hash
   104  func P2SHProgram(scriptHash []byte) ([]byte, error) {
   105  	builder := NewBuilder()
   106  	builder.AddOp(vm.OP_DUP)
   107  	builder.AddOp(vm.OP_SHA3)
   108  	builder.AddData(scriptHash)
   109  	builder.AddOp(vm.OP_EQUALVERIFY)
   110  	builder.AddUint64(0)
   111  	builder.AddOp(vm.OP_SWAP)
   112  	builder.AddUint64(0)
   113  	builder.AddOp(vm.OP_CHECKPREDICATE)
   114  	return builder.Build()
   115  }
   116  
   117  // P2SPMultiSigProgram generates the script for control transaction output
   118  func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
   119  	builder := NewBuilder()
   120  	if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
   121  		return nil, err
   122  	}
   123  	return builder.Build()
   124  }
   125  
   126  // P2SPMultiSigProgramWithHeight generates the script with block height for control transaction output
   127  func P2SPMultiSigProgramWithHeight(pubkeys []ed25519.PublicKey, nrequired int, blockHeight uint64) ([]byte, error) {
   128  	builder := NewBuilder()
   129  	if blockHeight > 0 {
   130  		builder.AddUint64(blockHeight)
   131  		builder.AddOp(vm.OP_BLOCKHEIGHT)
   132  		builder.AddOp(vm.OP_GREATERTHAN)
   133  		builder.AddOp(vm.OP_VERIFY)
   134  	}
   135  
   136  	if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
   137  		return nil, err
   138  	}
   139  	return builder.Build()
   140  }
   141  
   142  func checkMultiSigParams(nrequired, npubkeys int64) error {
   143  	if nrequired < 0 {
   144  		return errors.WithDetail(ErrBadValue, "negative quorum")
   145  	}
   146  	if npubkeys < 0 {
   147  		return errors.WithDetail(ErrBadValue, "negative pubkey count")
   148  	}
   149  	if nrequired > npubkeys {
   150  		return errors.WithDetail(ErrBadValue, "quorum too big")
   151  	}
   152  	if nrequired == 0 && npubkeys > 0 {
   153  		return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
   154  	}
   155  	return nil
   156  }
   157  
   158  // GetIssuanceProgramRestrictHeight return issuance program restrict height
   159  // if height invalid return 0
   160  func GetIssuanceProgramRestrictHeight(program []byte) uint64 {
   161  	insts, err := vm.ParseProgram(program)
   162  	if err != nil {
   163  		return 0
   164  	}
   165  
   166  	if len(insts) >= 4 && insts[0].IsPushdata() && insts[1].Op == vm.OP_BLOCKHEIGHT && insts[2].Op == vm.OP_GREATERTHAN && insts[3].Op == vm.OP_VERIFY {
   167  		heightInt, err := vm.AsBigInt(insts[0].Data)
   168  		if err != nil {
   169  			return 0
   170  		}
   171  
   172  		height, overflow := heightInt.Uint64WithOverflow()
   173  		if overflow {
   174  			return 0
   175  		}
   176  
   177  		return height
   178  	}
   179  	return 0
   180  }