github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/vm/vmutil/script.go (about)

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