7 "github.com/vapor/crypto/ed25519"
8 "github.com/vapor/crypto/ed25519/chainkd"
9 "github.com/vapor/errors"
10 "github.com/vapor/protocol/vm"
15 ErrBadValue = errors.New("bad value")
16 ErrMultisigFormat = errors.New("bad multisig program format")
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)
24 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
25 if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
29 b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
30 for _, p := range pubkeys {
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]
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()
46 // P2WPKHProgram return the segwit pay to public key hash
47 func P2WPKHProgram(hash []byte) ([]byte, error) {
48 builder := NewBuilder()
51 return builder.Build()
54 // P2WSHProgram return the segwit pay to script hash
55 func P2WSHProgram(hash []byte) ([]byte, error) {
56 builder := NewBuilder()
59 return builder.Build()
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)
69 return builder.Build()
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()
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)
93 builder.AddOp(vm.OP_SWAP)
95 builder.AddOp(vm.OP_CHECKPREDICATE)
96 return builder.Build()
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 {
105 return builder.Build()
108 // ParseP2SPMultiSigProgram is unknow for us yet
109 func ParseP2SPMultiSigProgram(program []byte) ([]ed25519.PublicKey, int, error) {
110 pops, err := vm.ParseProgram(program)
115 return nil, 0, vm.ErrShortProgram
118 // Count all instructions backwards from the end in case there are
119 // extra instructions at the beginning of the program (like a
122 npubkeys, err := vm.AsInt64(pops[len(pops)-6].Data)
126 if int(npubkeys) > len(pops)-10 {
127 return nil, 0, vm.ErrShortProgram
129 nrequired, err := vm.AsInt64(pops[len(pops)-7].Data)
133 err = checkMultiSigParams(nrequired, npubkeys)
138 firstPubkeyIndex := len(pops) - 7 - int(npubkeys)
140 pubkeys := make([]ed25519.PublicKey, 0, npubkeys)
141 for i := firstPubkeyIndex; i < firstPubkeyIndex+int(npubkeys); i++ {
142 if len(pops[i].Data) != ed25519.PublicKeySize {
145 pubkeys = append(pubkeys, ed25519.PublicKey(pops[i].Data))
147 return pubkeys, int(nrequired), nil
150 func checkMultiSigParams(nrequired, npubkeys int64) error {
152 return errors.WithDetail(ErrBadValue, "negative quorum")
155 return errors.WithDetail(ErrBadValue, "negative pubkey count")
157 if nrequired > npubkeys {
158 return errors.WithDetail(ErrBadValue, "quorum too big")
160 if nrequired == 0 && npubkeys > 0 {
161 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
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()
173 var pubkeys []ed25519.PublicKey
174 for _, xpub := range federationRedeemXPub {
175 // pub + scriptPubKey 生成一个随机数A
177 h := hmac.New(sha256.New, xpub[:])
179 tweak := h.Sum(tmp[:])
180 // pub + A 生成一个新的公钥pub_new
181 chaildXPub := xpub.Child(tweak)
182 pubkeys = append(pubkeys, chaildXPub.PublicKey())
184 federationRedeemScript, _ = P2SPMultiSigProgram(pubkeys, len(pubkeys))
186 return federationRedeemScript