OSDN Git Service

Hulk did something
[bytom/vapor.git] / protocol / vm / vmutil / script.go
1 package vmutil
2
3 import (
4         "github.com/vapor/crypto/ed25519"
5         "github.com/vapor/errors"
6         "github.com/vapor/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 }