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 }