10 log "github.com/sirupsen/logrus"
12 "github.com/vapor/account"
13 "github.com/vapor/blockchain/txbuilder"
14 "github.com/vapor/claim/bytom/mainchain"
15 bytomtypes "github.com/vapor/claim/bytom/protocolbc/types"
16 "github.com/vapor/claim/rpc"
17 "github.com/vapor/common"
18 "github.com/vapor/consensus"
19 "github.com/vapor/crypto/ed25519/chainkd"
20 chainjson "github.com/vapor/encoding/json"
21 "github.com/vapor/equity/pegin_contract"
22 "github.com/vapor/errors"
23 "github.com/vapor/protocol/bc"
24 "github.com/vapor/protocol/bc/types"
25 "github.com/vapor/protocol/vm/vmutil"
28 type BytomMainTx struct {
32 func (b *BytomMainTx) BuildMainChainTxForContract() ([]byte, error) {
33 var xpubs []chainkd.XPub
34 for _, pub := range b.Pubs {
35 xpub := chainkd.XPub{}
36 if err := xpub.UnmarshalText([]byte(pub)); err != nil {
39 // pub + scriptPubKey 生成一个随机数A
41 h := hmac.New(sha256.New, xpub[:])
42 h.Write(b.ClaimScript)
43 tweak := h.Sum(tmp[:])
44 // pub + A 生成一个新的公钥pub_new
45 chaildXPub := xpub.Child(tweak)
46 xpubs = append(xpubs, chaildXPub)
49 utxo := &account.UTXO{}
50 if err := json.Unmarshal([]byte(b.Utxo), utxo); err != nil {
53 txInput, sigInst, err := contractToInputs(utxo, xpubs, b.ClaimScript)
54 builder := mainchain.NewBuilder(time.Now())
55 builder.AddInput(txInput, sigInst)
56 changeAmount := uint64(0)
59 if err := tx.UnmarshalText([]byte(b.Tx)); err != nil {
62 for _, key := range tx.GetResultIds() {
63 output, err := tx.Retire(*key)
65 log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
69 var controlProgram []byte
71 if controlProgram, retBool = getInput(tx.Entries, *key, b.ControlProgram); !retBool {
72 return nil, errors.New("The corresponding input cannot be found")
75 assetID := *output.Source.Value.AssetId
76 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
77 builder.AddOutput(out)
78 changeAmount = utxo.Amount - output.Source.Value.Amount
82 return nil, errors.New("It's not a transaction to retire assets")
85 if changeAmount > 100000000 {
87 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, utxo.ControlProgram)
88 builder.AddOutput(out)
91 tmpl, txData, err := builder.Build()
96 for i, out := range tmpl.Transaction.Outputs {
97 if bytes.Equal(out.ControlProgram, utxo.ControlProgram) {
98 if changeAmount-100000000 > 0 {
99 txData.Outputs[i].Amount = changeAmount - 100000000
103 tmpl.Transaction = bytomtypes.NewTx(*txData)
104 resp, err := mainchain.MarshalText(tmpl)
111 func (b *BytomMainTx) BuildMainChainTx() ([]byte, error) {
112 var xpubs []chainkd.XPub
113 for _, pub := range b.Pubs {
115 xpub := chainkd.XPub{}
116 if err := xpub.UnmarshalText([]byte(pub)); err != nil {
119 // pub + scriptPubKey 生成一个随机数A
121 h := hmac.New(sha256.New, xpub[:])
122 h.Write(b.ClaimScript)
123 tweak := h.Sum(tmp[:])
124 // pub + A 生成一个新的公钥pub_new
125 chaildXPub := xpub.Child(tweak)
126 xpubs = append(xpubs, chaildXPub)
129 utxo := &account.UTXO{}
130 if err := json.Unmarshal([]byte(b.Utxo), utxo); err != nil {
134 txInput, sigInst, err := utxoToInputs(xpubs, utxo)
139 builder := mainchain.NewBuilder(time.Now())
140 builder.AddInput(txInput, sigInst)
141 changeAmount := uint64(0)
144 if err := tx.UnmarshalText([]byte(b.Tx)); err != nil {
147 for _, key := range tx.GetResultIds() {
148 output, err := tx.Retire(*key)
150 log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
154 var controlProgram []byte
156 if controlProgram, retBool = getInput(tx.Entries, *key, b.ControlProgram); !retBool {
157 return nil, errors.New("The corresponding input cannot be found")
160 assetID := *output.Source.Value.AssetId
161 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
162 builder.AddOutput(out)
163 changeAmount = utxo.Amount - output.Source.Value.Amount
168 return nil, errors.New("It's not a transaction to retire assets")
171 if changeAmount > 0 {
173 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, utxo.ControlProgram)
174 builder.AddOutput(out)
177 tmpl, txData, err := builder.Build()
183 txGasResp, err := EstimateTxGasForMainchain(*tmpl)
188 for i, out := range tmpl.Transaction.Outputs {
189 if bytes.Equal(out.ControlProgram, utxo.ControlProgram) {
190 //tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
191 tx.Outputs[i].Amount = changeAmount - 100000000
194 tmpl.Transaction = bytomtypes.NewTx(*txData)
195 resp, err := json.Marshal(tmpl)
204 type BytomMainSign struct {
208 func (b *BytomMainSign) SignWithKey() (interface{}, error) {
209 xprv := &chainkd.XPrv{}
210 if err := xprv.UnmarshalText([]byte(b.Xprv)); err != nil {
214 xpub := &chainkd.XPub{}
215 if err := xpub.UnmarshalText([]byte(b.XPub)); err != nil {
219 // pub + scriptPubKey 生成一个随机数A
221 h := hmac.New(sha256.New, xpub[:])
222 h.Write(b.ClaimScript)
223 tweak := h.Sum(tmp[:])
224 // pub + A 生成一个新的公钥pub_new
225 privateKey := xprv.Child(tweak, false)
227 txs := &mainchain.Template{}
228 if err := mainchain.UnmarshalText([]byte(b.Txs), txs); err != nil {
232 if err := sign(txs, privateKey); err != nil {
235 log.Info("Sign Transaction complete.")
237 Tx *mainchain.Template `json:"transaction"`
238 SignComplete bool `json:"sign_complete"`
239 }{Tx: txs, SignComplete: mainchain.SignProgress(txs)}, nil
242 func sign(tmpl *mainchain.Template, xprv chainkd.XPrv) error {
243 for i, sigInst := range tmpl.SigningInstructions {
244 for j, wc := range sigInst.WitnessComponents {
245 switch sw := wc.(type) {
246 case *mainchain.SignatureWitness:
247 err := sw.Sign(tmpl, uint32(i), xprv)
249 return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
251 case *mainchain.RawTxSigWitness:
252 err := sw.Sign(tmpl, uint32(i), xprv)
254 return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
259 return materializeWitnesses(tmpl)
262 func materializeWitnesses(txTemplate *mainchain.Template) error {
263 msg := txTemplate.Transaction
266 return errors.Wrap(txbuilder.ErrMissingRawTx)
269 if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
270 return errors.Wrap(txbuilder.ErrBadInstructionCount)
273 for i, sigInst := range txTemplate.SigningInstructions {
274 if msg.Inputs[sigInst.Position] == nil {
275 return errors.WithDetailf(txbuilder.ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
279 for j, wc := range sigInst.WitnessComponents {
280 err := wc.Materialize(&witness)
282 return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
285 msg.SetInputArguments(sigInst.Position, witness)
292 func getInput(entry map[bc.Hash]bc.Entry, outputID bc.Hash, controlProgram string) ([]byte, bool) {
293 output := entry[outputID].(*bc.Retirement)
294 mux := entry[*output.Source.Ref].(*bc.Mux)
296 for _, valueSource := range mux.GetSources() {
297 spend := entry[*valueSource.Ref].(*bc.Spend)
298 prevout := entry[*spend.SpentOutputId].(*bc.Output)
300 var ctrlProgram chainjson.HexBytes
301 ctrlProgram = prevout.ControlProgram.Code
302 tmp, _ := ctrlProgram.MarshalText()
303 if string(tmp) == controlProgram {
304 return ctrlProgram, true
310 // UtxoToInputs convert an utxo to the txinput
311 func utxoToInputs(xpubs []chainkd.XPub, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
312 txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
313 sigInst := &mainchain.SigningInstruction{}
316 sigInst.AddWitnessKeys(xpubs, quorum)
317 return txInput, sigInst, nil
320 address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
325 sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
327 switch address.(type) {
328 case *common.AddressWitnessPubKeyHash:
329 derivedPK := xpubs[0].PublicKey()
330 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness([]byte(derivedPK)))
332 case *common.AddressWitnessScriptHash:
333 derivedXPubs := xpubs
334 derivedPKs := chainkd.XPubKeys(derivedXPubs)
335 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
339 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(script))
342 return nil, nil, errors.New("unsupport address type")
345 return txInput, sigInst, nil
348 func contractToInputs(u *account.UTXO, xpubs []chainkd.XPub, ClaimScript chainjson.HexBytes) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
349 txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
350 sigInst := &mainchain.SigningInstruction{}
353 for _, xpub := range xpubs {
354 xpubsTmp := []chainkd.XPub{xpub}
355 sigInst.AddRawWitnessKeysWithoutPath(xpubsTmp, 1)
359 peginContractPrograms, err := pegin_contract.GetPeginContractPrograms(ClaimScript)
363 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(peginContractPrograms))
365 return txInput, sigInst, nil