OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / protocol / vm / vmutil / script.go
1 package vmutil
2
3 import (
4         "crypto/hmac"
5         "crypto/sha256"
6
7         "github.com/vapor/crypto/ed25519"
8         "github.com/vapor/crypto/ed25519/chainkd"
9         "github.com/vapor/errors"
10         "github.com/vapor/protocol/vm"
11 )
12
13 // pre-define errors
14 var (
15         ErrBadValue       = errors.New("bad value")
16         ErrMultisigFormat = errors.New("bad multisig program format")
17 )
18
19 // IsUnspendable checks if a contorl program is absolute failed
20 func IsUnspendable(prog []byte) bool {
21         return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
22 }
23
24 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
25         if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
26                 return err
27         }
28
29         b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
30         for _, p := range pubkeys {
31                 b.AddData(p)
32         }
33         b.AddInt64(int64(nrequired))    // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
34         b.AddInt64(int64(len(pubkeys))) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
35         b.AddOp(vm.OP_CHECKMULTISIG)    // stack is now [... NARGS]
36         return nil
37 }
38
39 // DefaultCoinbaseProgram generates the script for contorl coinbase output
40 func DefaultCoinbaseProgram() ([]byte, error) {
41         builder := NewBuilder()
42         builder.AddOp(vm.OP_TRUE)
43         return builder.Build()
44 }
45
46 // P2WPKHProgram return the segwit pay to public key hash
47 func P2WPKHProgram(hash []byte) ([]byte, error) {
48         builder := NewBuilder()
49         builder.AddInt64(0)
50         builder.AddData(hash)
51         return builder.Build()
52 }
53
54 // P2WSHProgram return the segwit pay to script hash
55 func P2WSHProgram(hash []byte) ([]byte, error) {
56         builder := NewBuilder()
57         builder.AddInt64(0)
58         builder.AddData(hash)
59         return builder.Build()
60 }
61
62 // RetireProgram generates the script for retire output
63 func RetireProgram(comment []byte) ([]byte, error) {
64         builder := NewBuilder()
65         builder.AddOp(vm.OP_FAIL)
66         if len(comment) != 0 {
67                 builder.AddData(comment)
68         }
69         return builder.Build()
70 }
71
72 // P2PKHSigProgram generates the script for control with pubkey hash
73 func P2PKHSigProgram(pubkeyHash []byte) ([]byte, error) {
74         builder := NewBuilder()
75         builder.AddOp(vm.OP_DUP)
76         builder.AddOp(vm.OP_HASH160)
77         builder.AddData(pubkeyHash)
78         builder.AddOp(vm.OP_EQUALVERIFY)
79         builder.AddOp(vm.OP_TXSIGHASH)
80         builder.AddOp(vm.OP_SWAP)
81         builder.AddOp(vm.OP_CHECKSIG)
82         return builder.Build()
83 }
84
85 // P2SHProgram generates the script for control with script hash
86 func P2SHProgram(scriptHash []byte) ([]byte, error) {
87         builder := NewBuilder()
88         builder.AddOp(vm.OP_DUP)
89         builder.AddOp(vm.OP_SHA3)
90         builder.AddData(scriptHash)
91         builder.AddOp(vm.OP_EQUALVERIFY)
92         builder.AddInt64(-1)
93         builder.AddOp(vm.OP_SWAP)
94         builder.AddInt64(0)
95         builder.AddOp(vm.OP_CHECKPREDICATE)
96         return builder.Build()
97 }
98
99 // P2SPMultiSigProgram generates the script for contorl transaction output
100 func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
101         builder := NewBuilder()
102         if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
103                 return nil, err
104         }
105         return builder.Build()
106 }
107
108 // ParseP2SPMultiSigProgram is unknow for us yet
109 func ParseP2SPMultiSigProgram(program []byte) ([]ed25519.PublicKey, int, error) {
110         pops, err := vm.ParseProgram(program)
111         if err != nil {
112                 return nil, 0, err
113         }
114         if len(pops) < 11 {
115                 return nil, 0, vm.ErrShortProgram
116         }
117
118         // Count all instructions backwards from the end in case there are
119         // extra instructions at the beginning of the program (like a
120         // <pushdata> DROP).
121
122         npubkeys, err := vm.AsInt64(pops[len(pops)-6].Data)
123         if err != nil {
124                 return nil, 0, err
125         }
126         if int(npubkeys) > len(pops)-10 {
127                 return nil, 0, vm.ErrShortProgram
128         }
129         nrequired, err := vm.AsInt64(pops[len(pops)-7].Data)
130         if err != nil {
131                 return nil, 0, err
132         }
133         err = checkMultiSigParams(nrequired, npubkeys)
134         if err != nil {
135                 return nil, 0, err
136         }
137
138         firstPubkeyIndex := len(pops) - 7 - int(npubkeys)
139
140         pubkeys := make([]ed25519.PublicKey, 0, npubkeys)
141         for i := firstPubkeyIndex; i < firstPubkeyIndex+int(npubkeys); i++ {
142                 if len(pops[i].Data) != ed25519.PublicKeySize {
143                         return nil, 0, err
144                 }
145                 pubkeys = append(pubkeys, ed25519.PublicKey(pops[i].Data))
146         }
147         return pubkeys, int(nrequired), nil
148 }
149
150 func checkMultiSigParams(nrequired, npubkeys int64) error {
151         if nrequired < 0 {
152                 return errors.WithDetail(ErrBadValue, "negative quorum")
153         }
154         if npubkeys < 0 {
155                 return errors.WithDetail(ErrBadValue, "negative pubkey count")
156         }
157         if nrequired > npubkeys {
158                 return errors.WithDetail(ErrBadValue, "quorum too big")
159         }
160         if nrequired == 0 && npubkeys > 0 {
161                 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
162         }
163         return nil
164 }
165
166 func CalculateContract(federationRedeemXPub []chainkd.XPub, claimScript []byte) []byte {
167         //func CalculateContract(federationRedeemScript []byte, scriptPubKey []byte) []byte {
168         //-fedpegscript=需要的公钥+公钥长度+公钥+……+公钥长度+公钥+公钥个数+OP_CHECKMULTISIG
169         var federationRedeemScript []byte
170         if len(federationRedeemXPub) == 0 {
171                 federationRedeemScript, _ = DefaultCoinbaseProgram()
172         } else {
173                 var pubkeys []ed25519.PublicKey
174                 for _, xpub := range federationRedeemXPub {
175                         // pub + scriptPubKey 生成一个随机数A
176                         var tmp [32]byte
177                         h := hmac.New(sha256.New, xpub[:])
178                         h.Write(claimScript)
179                         tweak := h.Sum(tmp[:])
180                         // pub +  A 生成一个新的公钥pub_new
181                         chaildXPub := xpub.Child(tweak)
182                         pubkeys = append(pubkeys, chaildXPub.PublicKey())
183                 }
184                 federationRedeemScript, _ = P2SPMultiSigProgram(pubkeys, len(pubkeys))
185         }
186         return federationRedeemScript
187 }