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 }