OSDN Git Service

module claim
[bytom/vapor.git] / api / main_transact.go
index 4bb63c8..fdce0b0 100644 (file)
 package api
 
 import (
-       "bytes"
-       "crypto/hmac"
-       "crypto/sha256"
-       "time"
-
-       log "github.com/sirupsen/logrus"
-
-       "github.com/vapor/account"
-       "github.com/vapor/blockchain/txbuilder"
-       "github.com/vapor/blockchain/txbuilder/mainchain"
-       "github.com/vapor/common"
-       "github.com/vapor/consensus"
-       "github.com/vapor/crypto/ed25519/chainkd"
+       "github.com/vapor/claim/rpc"
+       maintx "github.com/vapor/claim/rpc/bytom"
        chainjson "github.com/vapor/encoding/json"
-       "github.com/vapor/equity/pegin_contract"
-       "github.com/vapor/errors"
-       "github.com/vapor/protocol/bc"
-       "github.com/vapor/protocol/bc/types"
-       bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
-       "github.com/vapor/protocol/vm/vmutil"
 )
 
-func (a *API) buildMainChainTxForContract(ins struct {
-       Utxo           account.UTXO       `json:"utxo"`
-       Tx             types.Tx           `json:"raw_transaction"`
-       RootXPubs      []chainkd.XPub     `json:"root_xpubs"`
-       ControlProgram string             `json:"control_program"`
-       ClaimScript    chainjson.HexBytes `json:"claim_script"`
-}) Response {
-
-       var xpubs []chainkd.XPub
-       for _, xpub := range ins.RootXPubs {
-               // pub + scriptPubKey 生成一个随机数A
-               var tmp [32]byte
-               h := hmac.New(sha256.New, xpub[:])
-               h.Write(ins.ClaimScript)
-               tweak := h.Sum(tmp[:])
-               // pub +  A 生成一个新的公钥pub_new
-               chaildXPub := xpub.Child(tweak)
-               xpubs = append(xpubs, chaildXPub)
-       }
-
-       txInput, sigInst, err := contractToInputs(a, &ins.Utxo, xpubs, ins.ClaimScript)
-       builder := mainchain.NewBuilder(time.Now())
-       builder.AddInput(txInput, sigInst)
-       changeAmount := uint64(0)
-       retire := false
-       for _, key := range ins.Tx.GetResultIds() {
-               output, err := ins.Tx.Retire(*key)
-               if err != nil {
-                       log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
-                       continue
-               }
-               retire = true
-               var controlProgram []byte
-               retBool := true
-               if controlProgram, retBool = getInput(ins.Tx.Entries, *key, ins.ControlProgram); !retBool {
-                       return NewErrorResponse(errors.New("The corresponding input cannot be found"))
-               }
-
-               assetID := *output.Source.Value.AssetId
-               out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
-               builder.AddOutput(out)
-               changeAmount = ins.Utxo.Amount - output.Source.Value.Amount
-
-       }
-
-       if !retire {
-               return NewErrorResponse(errors.New("It's not a transaction to retire assets"))
-       }
-
-       if changeAmount > 0 {
-               u := ins.Utxo
-               out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, ins.Utxo.ControlProgram)
-               builder.AddOutput(out)
-       }
-
-       tmpl, tx, err := builder.Build()
-       if err != nil {
-               return NewErrorResponse(err)
-       }
-
-       for i, out := range tmpl.Transaction.Outputs {
-               if bytes.Equal(out.ControlProgram, ins.Utxo.ControlProgram) {
-                       //tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
-                       tx.Outputs[i].Amount = changeAmount - 100000000
-               }
-       }
-       tmpl.Transaction = bytomtypes.NewTx(*tx)
-       return NewSuccessResponse(tmpl)
+type mainTxResp struct {
+       Tx chainjson.HexBytes `json:"tx"`
 }
 
-func (a *API) buildMainChainTx(ins struct {
-       Utxo           account.UTXO       `json:"utxo"`
-       Tx             types.Tx           `json:"raw_transaction"`
-       RootXPubs      []chainkd.XPub     `json:"root_xpubs"`
-       ControlProgram string             `json:"control_program"`
-       ClaimScript    chainjson.HexBytes `json:"claim_script"`
-}) Response {
-
-       var xpubs []chainkd.XPub
-       for _, xpub := range ins.RootXPubs {
-               // pub + scriptPubKey 生成一个随机数A
-               var tmp [32]byte
-               h := hmac.New(sha256.New, xpub[:])
-               h.Write(ins.ClaimScript)
-               tweak := h.Sum(tmp[:])
-               // pub +  A 生成一个新的公钥pub_new
-               chaildXPub := xpub.Child(tweak)
-               xpubs = append(xpubs, chaildXPub)
+func (a *API) buildMainChainTxForContract(ins rpc.MainTxParam) Response {
+       main := &maintx.BytomMainTx{
+               MainTxParam: ins,
        }
 
-       txInput, sigInst, err := utxoToInputs(xpubs, &ins.Utxo)
-       if err != nil {
-               return NewErrorResponse(err)
-       }
-
-       builder := mainchain.NewBuilder(time.Now())
-       builder.AddInput(txInput, sigInst)
-       changeAmount := uint64(0)
-       retire := false
-       for _, key := range ins.Tx.GetResultIds() {
-               output, err := ins.Tx.Retire(*key)
-               if err != nil {
-                       log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
-                       continue
-               }
-               retire = true
-               var controlProgram []byte
-               retBool := true
-               if controlProgram, retBool = getInput(ins.Tx.Entries, *key, ins.ControlProgram); !retBool {
-                       return NewErrorResponse(errors.New("The corresponding input cannot be found"))
-               }
-
-               assetID := *output.Source.Value.AssetId
-               out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
-               builder.AddOutput(out)
-               changeAmount = ins.Utxo.Amount - output.Source.Value.Amount
-
-       }
-
-       if !retire {
-               return NewErrorResponse(errors.New("It's not a transaction to retire assets"))
-       }
-
-       if changeAmount > 0 {
-               u := ins.Utxo
-               out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, ins.Utxo.ControlProgram)
-               builder.AddOutput(out)
-       }
-
-       tmpl, tx, err := builder.Build()
-       if err != nil {
-               return NewErrorResponse(err)
-       }
-       //交易费估算
-       txGasResp, err := EstimateTxGasForMainchain(*tmpl)
+       resp, err := main.BuildMainChainTxForContract()
        if err != nil {
                return NewErrorResponse(err)
        }
-       for i, out := range tmpl.Transaction.Outputs {
-               if bytes.Equal(out.ControlProgram, ins.Utxo.ControlProgram) {
-                       tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
-               }
-       }
-       tmpl.Transaction = bytomtypes.NewTx(*tx)
-       return NewSuccessResponse(tmpl)
-}
-
-//
-func getInput(entry map[bc.Hash]bc.Entry, outputID bc.Hash, controlProgram string) ([]byte, bool) {
-       output := entry[outputID].(*bc.Retirement)
-       mux := entry[*output.Source.Ref].(*bc.Mux)
 
-       for _, valueSource := range mux.GetSources() {
-               spend := entry[*valueSource.Ref].(*bc.Spend)
-               prevout := entry[*spend.SpentOutputId].(*bc.Output)
-
-               var ctrlProgram chainjson.HexBytes
-               ctrlProgram = prevout.ControlProgram.Code
-               tmp, _ := ctrlProgram.MarshalText()
-               if string(tmp) == controlProgram {
-                       return ctrlProgram, true
-               }
-       }
-       return nil, false
+       return NewSuccessResponse(&mainTxResp{Tx: resp})
 }
 
-// UtxoToInputs convert an utxo to the txinput
-func utxoToInputs(xpubs []chainkd.XPub, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
-       txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
-       sigInst := &mainchain.SigningInstruction{}
-       quorum := len(xpubs)
-       if u.Address == "" {
-               sigInst.AddWitnessKeys(xpubs, quorum)
-               return txInput, sigInst, nil
+func (a *API) buildMainChainTx(ins rpc.MainTxParam) Response {
+       main := &maintx.BytomMainTx{
+               MainTxParam: ins,
        }
 
-       address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
+       resp, err := main.BuildMainChainTx()
        if err != nil {
-               return nil, nil, err
-       }
-
-       sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
-
-       switch address.(type) {
-       case *common.AddressWitnessPubKeyHash:
-               derivedPK := xpubs[0].PublicKey()
-               sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness([]byte(derivedPK)))
-
-       case *common.AddressWitnessScriptHash:
-               derivedXPubs := xpubs
-               derivedPKs := chainkd.XPubKeys(derivedXPubs)
-               script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
-               if err != nil {
-                       return nil, nil, err
-               }
-               sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(script))
-
-       default:
-               return nil, nil, errors.New("unsupport address type")
+               return NewErrorResponse(err)
        }
 
-       return txInput, sigInst, nil
+       return NewSuccessResponse(&mainTxResp{Tx: resp})
 }
 
-func contractToInputs(a *API, u *account.UTXO, xpubs []chainkd.XPub, ClaimScript chainjson.HexBytes) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
-       txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
-       sigInst := &mainchain.SigningInstruction{}
-
-       // raw_tx_signature
-       for _, xpub := range xpubs {
-               xpubsTmp := []chainkd.XPub{xpub}
-               sigInst.AddRawWitnessKeysWithoutPath(xpubsTmp, 1)
+func (a *API) signWithKey(ins rpc.MainTxSignParam) Response {
+       sign := maintx.BytomMainSign{
+               MainTxSignParam: ins,
        }
 
-       // data
-       peginContractPrograms, err := pegin_contract.GetPeginContractPrograms(ClaimScript)
+       resp, err := sign.SignWithKey()
        if err != nil {
-               return nil, nil, err
-       }
-       sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(peginContractPrograms))
-
-       return txInput, sigInst, nil
-}
-
-type signRespForMainchain struct {
-       Tx           *mainchain.Template `json:"transaction"`
-       SignComplete bool                `json:"sign_complete"`
-}
-
-func (a *API) signWithKey(ins struct {
-       Xprv        string             `json:"xprv"`
-       XPub        chainkd.XPub       `json:"xpub"`
-       Txs         mainchain.Template `json:"transaction"`
-       ClaimScript chainjson.HexBytes `json:"claim_script"`
-}) Response {
-       xprv := &chainkd.XPrv{}
-       if err := xprv.UnmarshalText([]byte(ins.Xprv)); err != nil {
                return NewErrorResponse(err)
        }
-       // pub + scriptPubKey 生成一个随机数A
-       var tmp [32]byte
-       h := hmac.New(sha256.New, ins.XPub[:])
-       h.Write(ins.ClaimScript)
-       tweak := h.Sum(tmp[:])
-       // pub +  A 生成一个新的公钥pub_new
-       privateKey := xprv.Child(tweak, false)
-
-       if err := sign(&ins.Txs, privateKey); err != nil {
-               return NewErrorResponse(err)
-       }
-       log.Info("Sign Transaction complete.")
-       log.Info(mainchain.SignProgress(&ins.Txs))
-       return NewSuccessResponse(&signRespForMainchain{Tx: &ins.Txs, SignComplete: mainchain.SignProgress(&ins.Txs)})
-}
-
-func sign(tmpl *mainchain.Template, xprv chainkd.XPrv) error {
-       for i, sigInst := range tmpl.SigningInstructions {
-               for j, wc := range sigInst.WitnessComponents {
-                       switch sw := wc.(type) {
-                       case *mainchain.SignatureWitness:
-                               err := sw.Sign(tmpl, uint32(i), xprv)
-                               if err != nil {
-                                       return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
-                               }
-                       case *mainchain.RawTxSigWitness:
-                               err := sw.Sign(tmpl, uint32(i), xprv)
-                               if err != nil {
-                                       return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
-                               }
-                       }
-               }
-       }
-       return materializeWitnesses(tmpl)
-}
-
-func materializeWitnesses(txTemplate *mainchain.Template) error {
-       msg := txTemplate.Transaction
-
-       if msg == nil {
-               return errors.Wrap(txbuilder.ErrMissingRawTx)
-       }
-
-       if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
-               return errors.Wrap(txbuilder.ErrBadInstructionCount)
-       }
-
-       for i, sigInst := range txTemplate.SigningInstructions {
-               if msg.Inputs[sigInst.Position] == nil {
-                       return errors.WithDetailf(txbuilder.ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
-               }
-
-               var witness [][]byte
-               for j, wc := range sigInst.WitnessComponents {
-                       err := wc.Materialize(&witness)
-                       if err != nil {
-                               return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
-                       }
-               }
-               msg.SetInputArguments(sigInst.Position, witness)
-       }
-
-       return nil
+       return NewSuccessResponse(resp)
 }