OSDN Git Service

add query API
[bytom/bytom.git] / protocol / vm / vmutil / script.go
1 package vmutil
2
3 import (
4         "github.com/bytom/crypto/ed25519"
5         "github.com/bytom/errors"
6         "github.com/bytom/protocol/vm"
7 )
8
9 var (
10         ErrBadValue       = errors.New("bad value")
11         ErrMultisigFormat = errors.New("bad multisig program format")
12 )
13
14 func IsUnspendable(prog []byte) bool {
15         return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
16 }
17
18 func ParseBlockMultiSigProgram(script []byte) ([]ed25519.PublicKey, int, error) {
19         pops, err := vm.ParseProgram(script)
20         if err != nil {
21                 return nil, 0, err
22         }
23         if len(pops) < 4 {
24                 return nil, 0, vm.ErrShortProgram
25         }
26         if pops[len(pops)-1].Op != vm.OP_CHECKMULTISIG {
27                 return nil, 0, errors.Wrap(ErrMultisigFormat, "no OP_CHECKMULTISIG")
28         }
29         npubkeys, err := vm.AsInt64(pops[len(pops)-2].Data)
30         if err != nil {
31                 return nil, 0, errors.Wrap(ErrMultisigFormat, "parsing npubkeys")
32         }
33         if int(npubkeys) != len(pops)-4 {
34                 return nil, 0, vm.ErrShortProgram
35         }
36         nrequired, err := vm.AsInt64(pops[len(pops)-3].Data)
37         if err != nil {
38                 return nil, 0, errors.Wrap(ErrMultisigFormat, "parsing nrequired")
39         }
40         err = checkMultiSigParams(nrequired, npubkeys)
41         if err != nil {
42                 return nil, 0, err
43         }
44
45         firstPubkeyIndex := len(pops) - 3 - int(npubkeys)
46
47         pubkeys := make([]ed25519.PublicKey, 0, npubkeys)
48         for i := firstPubkeyIndex; i < firstPubkeyIndex+int(npubkeys); i++ {
49                 if len(pops[i].Data) != ed25519.PublicKeySize {
50                         return nil, 0, err
51                 }
52                 pubkeys = append(pubkeys, ed25519.PublicKey(pops[i].Data))
53         }
54         return pubkeys, int(nrequired), nil
55 }
56
57 func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
58         err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys)))
59         if err != nil {
60                 return nil, err
61         }
62         builder := NewBuilder()
63         // Expected stack: [... NARGS SIG SIG SIG PREDICATE]
64         // Number of sigs must match nrequired.
65         builder.AddOp(vm.OP_DUP).AddOp(vm.OP_TOALTSTACK) // stash a copy of the predicate
66         builder.AddOp(vm.OP_SHA3)                        // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
67         for _, p := range pubkeys {
68                 builder.AddData(p)
69         }
70         builder.AddInt64(int64(nrequired))                     // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
71         builder.AddInt64(int64(len(pubkeys)))                  // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
72         builder.AddOp(vm.OP_CHECKMULTISIG).AddOp(vm.OP_VERIFY) // stack is now [... NARGS]
73         builder.AddOp(vm.OP_FROMALTSTACK)                      // stack is now [... NARGS PREDICATE]
74         builder.AddInt64(0).AddOp(vm.OP_CHECKPREDICATE)
75         return builder.Build()
76 }
77
78 func ParseP2SPMultiSigProgram(program []byte) ([]ed25519.PublicKey, int, error) {
79         pops, err := vm.ParseProgram(program)
80         if err != nil {
81                 return nil, 0, err
82         }
83         if len(pops) < 11 {
84                 return nil, 0, vm.ErrShortProgram
85         }
86
87         // Count all instructions backwards from the end in case there are
88         // extra instructions at the beginning of the program (like a
89         // <pushdata> DROP).
90
91         npubkeys, err := vm.AsInt64(pops[len(pops)-6].Data)
92         if err != nil {
93                 return nil, 0, err
94         }
95         if int(npubkeys) > len(pops)-10 {
96                 return nil, 0, vm.ErrShortProgram
97         }
98         nrequired, err := vm.AsInt64(pops[len(pops)-7].Data)
99         if err != nil {
100                 return nil, 0, err
101         }
102         err = checkMultiSigParams(nrequired, npubkeys)
103         if err != nil {
104                 return nil, 0, err
105         }
106
107         firstPubkeyIndex := len(pops) - 7 - int(npubkeys)
108
109         pubkeys := make([]ed25519.PublicKey, 0, npubkeys)
110         for i := firstPubkeyIndex; i < firstPubkeyIndex+int(npubkeys); i++ {
111                 if len(pops[i].Data) != ed25519.PublicKeySize {
112                         return nil, 0, err
113                 }
114                 pubkeys = append(pubkeys, ed25519.PublicKey(pops[i].Data))
115         }
116         return pubkeys, int(nrequired), nil
117 }
118
119 func checkMultiSigParams(nrequired, npubkeys int64) error {
120         if nrequired < 0 {
121                 return errors.WithDetail(ErrBadValue, "negative quorum")
122         }
123         if npubkeys < 0 {
124                 return errors.WithDetail(ErrBadValue, "negative pubkey count")
125         }
126         if nrequired > npubkeys {
127                 return errors.WithDetail(ErrBadValue, "quorum too big")
128         }
129         if nrequired == 0 && npubkeys > 0 {
130                 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
131         }
132         return nil
133 }