OSDN Git Service

Merge pull request #19 from Bytom/dev_modify_pegin_address_to_contract
authorwz <mars@bytom.io>
Thu, 29 Nov 2018 04:13:43 +0000 (12:13 +0800)
committerGitHub <noreply@github.com>
Thu, 29 Nov 2018 04:13:43 +0000 (12:13 +0800)
Dev modify pegin address to contract

account/accounts.go
api/api.go
api/cliam_transact.go [new file with mode: 0644]
api/main_transact.go [new file with mode: 0644]
api/receivers.go
api/transact.go
blockchain/txbuilder/mainchain/rawtxsig_witness.go
blockchain/txbuilder/mainchain/signing_instruction.go
node/node.go
tools/side_chain_tool/app/api/transaction.py
tools/side_chain_tool/sidechain.sh

index 503939b..0104183 100644 (file)
@@ -2,7 +2,11 @@
 package account
 
 import (
+       "crypto/hmac"
+       "crypto/sha256"
+       "encoding/hex"
        "encoding/json"
+       "fmt"
        "reflect"
        "sort"
        "strings"
@@ -18,8 +22,11 @@ import (
        "github.com/vapor/consensus"
        "github.com/vapor/consensus/segwit"
        "github.com/vapor/crypto"
+       "github.com/vapor/crypto/ed25519"
        "github.com/vapor/crypto/ed25519/chainkd"
        "github.com/vapor/crypto/sha3pool"
+       chainjson "github.com/vapor/encoding/json"
+       "github.com/vapor/equity/compiler"
        "github.com/vapor/errors"
        "github.com/vapor/protocol"
        "github.com/vapor/protocol/bc"
@@ -808,7 +815,80 @@ func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) {
        federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
        scriptHash := crypto.Sha256(federationRedeemScript)
 
-       address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
+       address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
+       if err != nil {
+               return "", nil
+       }
+
+       redeemContract := address.ScriptAddress()
+
+       program := []byte{}
+       program, err = vmutil.P2WSHProgram(redeemContract)
+       if err != nil {
+               return "", nil
+       }
+
+       return address.EncodeAddress(), program
+}
+
+var lockWith2of3KeysFmt = `
+contract LockWith2of3Keys(%s) locks amount of asset {
+  clause unlockWith2Sigs(%s) {
+    verify checkTxMultiSig(%s)
+    unlock amount of asset
+  }
+}
+`
+
+func (m *Manager) CreatePeginContractPrograms(accountID string, change bool) (string, []byte, error) {
+       // 通过配置获取
+       claimCtrlProg, err := m.CreateAddress(accountID, change)
+       if err != nil {
+               return "", nil, err
+       }
+       claimScript := claimCtrlProg.ControlProgram
+
+       peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
+       if err != nil {
+               return "", nil, err
+       }
+       return hex.EncodeToString(peginContractPrograms), claimScript, nil
+
+}
+
+func (m *Manager) CreatePeginContractAddress(accountID string, change bool) (string, []byte, error) {
+       // 通过配置获取
+       claimCtrlProg, err := m.CreateAddress(accountID, change)
+       if err != nil {
+               return "", nil, err
+       }
+       claimScript := claimCtrlProg.ControlProgram
+
+       peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
+       if err != nil {
+               return "", nil, err
+       }
+
+       scriptHash := crypto.Sha256(peginContractPrograms)
+
+       address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
+       if err != nil {
+               return "", nil, err
+       }
+
+       return address.EncodeAddress(), claimScript, nil
+
+}
+
+func (m *Manager) GetPeginContractControlPrograms(claimScript []byte) (string, []byte) {
+
+       peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
+       if err != nil {
+               return "", nil
+       }
+       scriptHash := crypto.Sha256(peginContractPrograms)
+
+       address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
        if err != nil {
                return "", nil
        }
@@ -823,3 +903,114 @@ func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) {
 
        return address.EncodeAddress(), program
 }
+
+func (m *Manager) GetPeginContractPrograms(claimScript []byte) ([]byte, error) {
+
+       pubkeys := getNewXpub(consensus.ActiveNetParams.FedpegXPubs, claimScript)
+       num := len(pubkeys)
+       if num == 0 {
+               return nil, errors.New("Fedpeg's XPubs is empty")
+       }
+       params := ""
+       unlockParams := ""
+       checkParams := "["
+
+       for index := 0; index < num; index++ {
+               param := fmt.Sprintf("pubkey%d", index+1)
+               params += param
+               checkParams += param
+               if (index + 1) < num {
+                       params += ","
+                       checkParams += ","
+               }
+       }
+       params += ": PublicKey"
+       checkParams += "],["
+
+       signNum := getNumberOfSignaturesRequired(pubkeys)
+       for index := 0; index < signNum; index++ {
+               param := fmt.Sprintf("sig%d", index+1)
+               unlockParams += param
+               checkParams += param
+               if index+1 < signNum {
+                       unlockParams += ","
+                       checkParams += ","
+               }
+       }
+
+       unlockParams += ": Signature"
+       checkParams += "]"
+
+       lockWith2of3Keys := fmt.Sprintf(lockWith2of3KeysFmt, params, unlockParams, checkParams)
+       r := strings.NewReader(lockWith2of3Keys)
+       compiled, err := compiler.Compile(r)
+       if err != nil {
+               return nil, errors.New("Compile contract failed")
+       }
+
+       contract := compiled[len(compiled)-1]
+
+       if num < len(contract.Params) {
+               return nil, errors.New("Compile contract failed")
+       }
+
+       contractArgs, err := convertArguments(contract, pubkeys)
+       if err != nil {
+               fmt.Println("Convert arguments into contract parameters error:", err)
+               return nil, errors.New("Convert arguments into contract parameters error")
+       }
+
+       instantProg, err := instantiateContract(contract, contractArgs)
+       if err != nil {
+               fmt.Println("Instantiate contract error:", err)
+               return nil, errors.New("Instantiate contract error")
+       }
+
+       return instantProg, nil
+}
+
+func getNewXpub(federationRedeemXPub []chainkd.XPub, claimScript []byte) []ed25519.PublicKey {
+
+       var pubkeys []ed25519.PublicKey
+       for _, xpub := range federationRedeemXPub {
+               // pub + scriptPubKey 生成一个随机数A
+               var tmp [32]byte
+               h := hmac.New(sha256.New, xpub[:])
+               h.Write(claimScript)
+               tweak := h.Sum(tmp[:])
+               // pub +  A 生成一个新的公钥pub_new
+               chaildXPub := xpub.Child(tweak)
+               pubkeys = append(pubkeys, chaildXPub.PublicKey())
+       }
+       return pubkeys
+}
+
+func getNumberOfSignaturesRequired(pubkeys []ed25519.PublicKey) int {
+       return len(pubkeys)/2 + 1
+}
+
+// InstantiateContract instantiate contract parameters
+func instantiateContract(contract *compiler.Contract, args []compiler.ContractArg) ([]byte, error) {
+       program, err := compiler.Instantiate(contract.Body, contract.Params, contract.Recursive, args)
+       if err != nil {
+               return nil, err
+       }
+
+       return program, nil
+}
+
+func convertArguments(contract *compiler.Contract, args []ed25519.PublicKey) ([]compiler.ContractArg, error) {
+       var contractArgs []compiler.ContractArg
+       for i, p := range contract.Params {
+               var argument compiler.ContractArg
+               switch p.Type {
+               case "PublicKey":
+                       argument.S = (*chainjson.HexBytes)(&args[i])
+               default:
+                       return nil, errors.New("Contract parameter type error")
+               }
+               contractArgs = append(contractArgs, argument)
+       }
+
+       return contractArgs, nil
+}
index 940d6d2..d3a5790 100644 (file)
@@ -248,6 +248,7 @@ func (a *API) buildHandler() {
                m.Handle("/recovery-wallet", jsonHandler(a.recoveryFromRootXPubs))
 
                m.Handle("/get-pegin-address", jsonHandler(a.getPeginAddress))
+               m.Handle("/get-pegin-contract", jsonHandler(a.getPeginContract))
                m.Handle("/claim-pegin-transaction", jsonHandler(a.claimPeginTx))
                m.Handle("/create-key-pair", jsonHandler(a.createXKeys))
                m.Handle("/get-utxo-from-transaction", jsonHandler(a.getUnspentOutputs))
diff --git a/api/cliam_transact.go b/api/cliam_transact.go
new file mode 100644 (file)
index 0000000..413968d
--- /dev/null
@@ -0,0 +1,406 @@
+package api
+
+import (
+       "bytes"
+       "context"
+       "encoding/json"
+       "strconv"
+       "time"
+
+       log "github.com/sirupsen/logrus"
+       "github.com/vapor/account"
+       "github.com/vapor/blockchain/txbuilder"
+       "github.com/vapor/consensus"
+       "github.com/vapor/crypto/sha3pool"
+       chainjson "github.com/vapor/encoding/json"
+       "github.com/vapor/errors"
+       "github.com/vapor/protocol/bc"
+       "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/bc/types/bytom"
+       bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
+       "github.com/vapor/util"
+)
+
+func getPeginTxnOutputIndex(rawTx bytomtypes.Tx, controlProg []byte) int {
+       for index, output := range rawTx.Outputs {
+               if bytes.Equal(output.ControlProgram, controlProg) {
+                       return index
+               }
+       }
+       return 0
+}
+
+func toHash(hexBytes []chainjson.HexBytes) (hashs []*bytom.Hash) {
+       for _, data := range hexBytes {
+               b32 := [32]byte{}
+               copy(b32[:], data)
+               res := bytom.NewHash(b32)
+               hashs = append(hashs, &res)
+       }
+       return
+}
+
+func (a *API) claimPeginTx(ctx context.Context, ins struct {
+       Password     string                 `json:"password"`
+       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
+       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
+       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
+       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
+       Flags        []uint32               `json:"flags"`
+       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
+       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
+}) Response {
+       tmpl, err := a.createRawPegin(ctx, ins)
+       if err != nil {
+               log.WithField("build err", err).Error("fail on createrawpegin.")
+               return NewErrorResponse(err)
+       }
+       // 交易签名
+       if err := txbuilder.Sign(ctx, tmpl, ins.Password, a.PseudohsmSignTemplate); err != nil {
+               log.WithField("build err", err).Error("fail on sign transaction.")
+               return NewErrorResponse(err)
+       }
+
+       // submit
+       if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
+               return NewErrorResponse(err)
+       }
+
+       log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
+       return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
+}
+
+// GetMerkleBlockResp is resp struct for GetTxOutProof API
+type GetMerkleBlock struct {
+       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
+       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
+       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
+       Flags        []uint32               `json:"flags"`
+       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
+}
+
+func (a *API) createRawPegin(ctx context.Context, ins struct {
+       Password     string                 `json:"password"`
+       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
+       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
+       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
+       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
+       Flags        []uint32               `json:"flags"`
+       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
+       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
+}) (*txbuilder.Template, error) {
+       // proof验证
+       var flags []uint8
+       for flag := range ins.Flags {
+               flags = append(flags, uint8(flag))
+       }
+       txHashes := toHash(ins.TxHashes)
+       matchedTxIDs := toHash(ins.MatchedTxIDs)
+       if !bytomtypes.ValidateTxMerkleTreeProof(txHashes, flags, matchedTxIDs, ins.BlockHeader.BlockCommitment.TransactionsMerkleRoot) {
+               return nil, errors.New("Merkleblock validation failed")
+       }
+       // CheckBytomProof
+       //difficulty.CheckBytomProofOfWork(ins.BlockHeader.Hash(), ins.BlockHeader)
+       // 增加spv验证以及连接主链api查询交易的确认数
+       if util.ValidatePegin {
+               if err := util.IsConfirmedBytomBlock(ins.BlockHeader.Height, consensus.ActiveNetParams.PeginMinDepth); err != nil {
+                       return nil, err
+               }
+       }
+       // 找出与claim script有关联的交易的输出
+       var claimScript []byte
+       nOut := len(ins.RawTx.Outputs)
+       if ins.ClaimScript == nil {
+               // 遍历寻找与交易输出有关的claim script
+               cps, err := a.wallet.AccountMgr.ListControlProgram()
+               if err != nil {
+                       return nil, err
+               }
+
+               for _, cp := range cps {
+                       _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
+                       if controlProg == nil {
+                               continue
+                       }
+                       // 获取交易的输出
+                       nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
+                       if nOut != len(ins.RawTx.Outputs) {
+                               claimScript = cp.ControlProgram
+                       }
+               }
+       } else {
+               claimScript = ins.ClaimScript
+               _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
+               // 获取交易的输出
+               nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
+       }
+       if nOut == len(ins.RawTx.Outputs) {
+               return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
+       }
+
+       // 根据ClaimScript 获取account id
+       var hash [32]byte
+       sha3pool.Sum256(hash[:], claimScript)
+       data := a.wallet.DB.Get(account.ContractKey(hash))
+       if data == nil {
+               return nil, errors.New("Failed to find control program through claim script")
+       }
+
+       cp := &account.CtrlProgram{}
+       if err := json.Unmarshal(data, cp); err != nil {
+               return nil, errors.New("Failed on unmarshal control program")
+       }
+
+       // 构造交易
+       // 用输出作为交易输入 生成新的交易
+       builder := txbuilder.NewBuilder(time.Now())
+       // TODO 根据raw tx生成一个utxo
+       //txInput := types.NewClaimInputInput(nil, *ins.RawTx.Outputs[nOut].AssetId, ins.RawTx.Outputs[nOut].Amount, cp.ControlProgram)
+       assetId := bc.AssetID{}
+       assetId.V0 = ins.RawTx.Outputs[nOut].AssetId.GetV0()
+       assetId.V1 = ins.RawTx.Outputs[nOut].AssetId.GetV1()
+       assetId.V2 = ins.RawTx.Outputs[nOut].AssetId.GetV2()
+       assetId.V3 = ins.RawTx.Outputs[nOut].AssetId.GetV3()
+
+       sourceID := bc.Hash{}
+       sourceID.V0 = ins.RawTx.OutputID(nOut).GetV0()
+       sourceID.V1 = ins.RawTx.OutputID(nOut).GetV1()
+       sourceID.V2 = ins.RawTx.OutputID(nOut).GetV2()
+       sourceID.V3 = ins.RawTx.OutputID(nOut).GetV3()
+       outputAccount := ins.RawTx.Outputs[nOut].Amount
+
+       txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
+       if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
+               return nil, err
+       }
+       program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
+       if err != nil {
+               return nil, err
+       }
+
+       if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
+               return nil, err
+       }
+
+       tmpl, txData, err := builder.Build()
+       if err != nil {
+               return nil, err
+       }
+
+       // todo把一些主链的信息加到交易的stack中
+       var stack [][]byte
+
+       //amount
+       amount := strconv.FormatUint(ins.RawTx.Outputs[nOut].Amount, 10)
+       stack = append(stack, []byte(amount))
+       // 主链的gennesisBlockHash
+       stack = append(stack, []byte(consensus.ActiveNetParams.ParentGenesisBlockHash))
+       // claim script
+       stack = append(stack, claimScript)
+       // raw tx
+       tx, _ := json.Marshal(ins.RawTx)
+       stack = append(stack, tx)
+       // proof
+       MerkleBLock := GetMerkleBlock{
+               BlockHeader:  ins.BlockHeader,
+               TxHashes:     ins.TxHashes,
+               StatusHashes: ins.StatusHashes,
+               Flags:        ins.Flags,
+               MatchedTxIDs: ins.MatchedTxIDs,
+       }
+       txOutProof, _ := json.Marshal(MerkleBLock)
+       stack = append(stack, txOutProof)
+
+       //      tmpl.Transaction.Inputs[0].Peginwitness = stack
+       txData.Inputs[0].Peginwitness = stack
+
+       //交易费估算
+       txGasResp, err := EstimateTxGas(*tmpl)
+       if err != nil {
+               return nil, err
+       }
+       txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
+       //重设置Transaction
+       tmpl.Transaction = types.NewTx(*txData)
+       return tmpl, nil
+}
+
+func (a *API) claimContractPeginTx(ctx context.Context, ins struct {
+       Password     string                 `json:"password"`
+       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
+       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
+       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
+       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
+       Flags        []uint32               `json:"flags"`
+       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
+       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
+}) Response {
+       tmpl, err := a.createContractRawPegin(ctx, ins)
+       if err != nil {
+               log.WithField("build err", err).Error("fail on createrawpegin.")
+               return NewErrorResponse(err)
+       }
+       // 交易签名
+       if err := txbuilder.Sign(ctx, tmpl, ins.Password, a.PseudohsmSignTemplate); err != nil {
+               log.WithField("build err", err).Error("fail on sign transaction.")
+               return NewErrorResponse(err)
+       }
+
+       // submit
+       if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
+               return NewErrorResponse(err)
+       }
+
+       log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
+       return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
+}
+
+func (a *API) createContractRawPegin(ctx context.Context, ins struct {
+       Password     string                 `json:"password"`
+       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
+       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
+       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
+       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
+       Flags        []uint32               `json:"flags"`
+       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
+       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
+}) (*txbuilder.Template, error) {
+       // proof验证
+       var flags []uint8
+       for flag := range ins.Flags {
+               flags = append(flags, uint8(flag))
+       }
+       txHashes := toHash(ins.TxHashes)
+       matchedTxIDs := toHash(ins.MatchedTxIDs)
+       if !bytomtypes.ValidateTxMerkleTreeProof(txHashes, flags, matchedTxIDs, ins.BlockHeader.BlockCommitment.TransactionsMerkleRoot) {
+               return nil, errors.New("Merkleblock validation failed")
+       }
+       // CheckBytomProof
+       //difficulty.CheckBytomProofOfWork(ins.BlockHeader.Hash(), ins.BlockHeader)
+       // 增加spv验证以及连接主链api查询交易的确认数
+       if util.ValidatePegin {
+               if err := util.IsConfirmedBytomBlock(ins.BlockHeader.Height, consensus.ActiveNetParams.PeginMinDepth); err != nil {
+                       return nil, err
+               }
+       }
+       // 找出与claim script有关联的交易的输出
+       var claimScript []byte
+       nOut := len(ins.RawTx.Outputs)
+       if ins.ClaimScript == nil {
+               // 遍历寻找与交易输出有关的claim script
+               cps, err := a.wallet.AccountMgr.ListControlProgram()
+               if err != nil {
+                       return nil, err
+               }
+
+               for _, cp := range cps {
+                       controlProg, err := a.wallet.AccountMgr.GetPeginContractPrograms(cp.ControlProgram)
+
+                       if controlProg == nil || err != nil {
+                               continue
+                       }
+                       // 获取交易的输出
+                       nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
+                       if nOut != len(ins.RawTx.Outputs) {
+                               claimScript = cp.ControlProgram
+                       }
+               }
+       } else {
+               claimScript = ins.ClaimScript
+               controlProg, err := a.wallet.AccountMgr.GetPeginContractPrograms(claimScript)
+               if err != nil {
+                       return nil, err
+               }
+               // 获取交易的输出
+               nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
+       }
+       if nOut == len(ins.RawTx.Outputs) {
+               return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
+       }
+
+       // 根据ClaimScript 获取account id
+       var hash [32]byte
+       sha3pool.Sum256(hash[:], claimScript)
+       data := a.wallet.DB.Get(account.ContractKey(hash))
+       if data == nil {
+               return nil, errors.New("Failed to find control program through claim script")
+       }
+
+       cp := &account.CtrlProgram{}
+       if err := json.Unmarshal(data, cp); err != nil {
+               return nil, errors.New("Failed on unmarshal control program")
+       }
+
+       // 构造交易
+       // 用输出作为交易输入 生成新的交易
+       builder := txbuilder.NewBuilder(time.Now())
+       // TODO 根据raw tx生成一个utxo
+       //txInput := types.NewClaimInputInput(nil, *ins.RawTx.Outputs[nOut].AssetId, ins.RawTx.Outputs[nOut].Amount, cp.ControlProgram)
+       assetId := bc.AssetID{}
+       assetId.V0 = ins.RawTx.Outputs[nOut].AssetId.GetV0()
+       assetId.V1 = ins.RawTx.Outputs[nOut].AssetId.GetV1()
+       assetId.V2 = ins.RawTx.Outputs[nOut].AssetId.GetV2()
+       assetId.V3 = ins.RawTx.Outputs[nOut].AssetId.GetV3()
+
+       sourceID := bc.Hash{}
+       sourceID.V0 = ins.RawTx.OutputID(nOut).GetV0()
+       sourceID.V1 = ins.RawTx.OutputID(nOut).GetV1()
+       sourceID.V2 = ins.RawTx.OutputID(nOut).GetV2()
+       sourceID.V3 = ins.RawTx.OutputID(nOut).GetV3()
+       outputAccount := ins.RawTx.Outputs[nOut].Amount
+
+       txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
+       if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
+               return nil, err
+       }
+       program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
+       if err != nil {
+               return nil, err
+       }
+
+       if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
+               return nil, err
+       }
+
+       tmpl, txData, err := builder.Build()
+       if err != nil {
+               return nil, err
+       }
+
+       // todo把一些主链的信息加到交易的stack中
+       var stack [][]byte
+
+       //amount
+       amount := strconv.FormatUint(ins.RawTx.Outputs[nOut].Amount, 10)
+       stack = append(stack, []byte(amount))
+       // 主链的gennesisBlockHash
+       stack = append(stack, []byte(consensus.ActiveNetParams.ParentGenesisBlockHash))
+       // claim script
+       stack = append(stack, claimScript)
+       // raw tx
+       tx, _ := json.Marshal(ins.RawTx)
+       stack = append(stack, tx)
+       // proof
+       MerkleBLock := GetMerkleBlock{
+               BlockHeader:  ins.BlockHeader,
+               TxHashes:     ins.TxHashes,
+               StatusHashes: ins.StatusHashes,
+               Flags:        ins.Flags,
+               MatchedTxIDs: ins.MatchedTxIDs,
+       }
+       txOutProof, _ := json.Marshal(MerkleBLock)
+       stack = append(stack, txOutProof)
+
+       //      tmpl.Transaction.Inputs[0].Peginwitness = stack
+       txData.Inputs[0].Peginwitness = stack
+
+       //交易费估算
+       txGasResp, err := EstimateTxGas(*tmpl)
+       if err != nil {
+               return nil, err
+       }
+       txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
+       //重设置Transaction
+       tmpl.Transaction = types.NewTx(*txData)
+       return tmpl, nil
+}
diff --git a/api/main_transact.go b/api/main_transact.go
new file mode 100644 (file)
index 0000000..6a35d45
--- /dev/null
@@ -0,0 +1,368 @@
+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"
+       chainjson "github.com/vapor/encoding/json"
+       "github.com/vapor/errors"
+       "github.com/vapor/protocol/bc"
+       "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/bc/types/bytom"
+       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)
+       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 := bytom.AssetID{
+                       V0: output.Source.Value.AssetId.GetV0(),
+                       V1: output.Source.Value.AssetId.GetV1(),
+                       V2: output.Source.Value.AssetId.GetV2(),
+                       V3: output.Source.Value.AssetId.GetV3(),
+               }
+               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
+               assetID := bytom.AssetID{
+                       V0: u.AssetID.GetV0(),
+                       V1: u.AssetID.GetV1(),
+                       V2: u.AssetID.GetV2(),
+                       V3: u.AssetID.GetV3(),
+               }
+               out := bytomtypes.NewTxOutput(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)
+}
+
+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)
+       }
+
+       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 := bytom.AssetID{
+                       V0: output.Source.Value.AssetId.GetV0(),
+                       V1: output.Source.Value.AssetId.GetV1(),
+                       V2: output.Source.Value.AssetId.GetV2(),
+                       V3: output.Source.Value.AssetId.GetV3(),
+               }
+               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
+               assetID := bytom.AssetID{
+                       V0: u.AssetID.GetV0(),
+                       V1: u.AssetID.GetV1(),
+                       V2: u.AssetID.GetV2(),
+                       V3: u.AssetID.GetV3(),
+               }
+               out := bytomtypes.NewTxOutput(assetID, changeAmount, ins.Utxo.ControlProgram)
+               builder.AddOutput(out)
+       }
+
+       tmpl, tx, err := builder.Build()
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+       //交易费估算
+       txGasResp, err := EstimateTxGasForMainchain(*tmpl)
+       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
+}
+
+// UtxoToInputs convert an utxo to the txinput
+func utxoToInputs(xpubs []chainkd.XPub, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
+       sourceID := bytom.Hash{
+               V0: u.SourceID.GetV0(),
+               V1: u.SourceID.GetV1(),
+               V2: u.SourceID.GetV2(),
+               V3: u.SourceID.GetV3(),
+       }
+
+       assetID := bytom.AssetID{
+               V0: u.AssetID.GetV0(),
+               V1: u.AssetID.GetV1(),
+               V2: u.AssetID.GetV2(),
+               V3: u.AssetID.GetV3(),
+       }
+
+       txInput := bytomtypes.NewSpendInput(nil, sourceID, assetID, u.Amount, u.SourcePos, u.ControlProgram)
+       sigInst := &mainchain.SigningInstruction{}
+       quorum := len(xpubs)
+       if u.Address == "" {
+               sigInst.AddWitnessKeys(xpubs, quorum)
+               return txInput, sigInst, nil
+       }
+
+       address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
+       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 txInput, sigInst, nil
+}
+
+func contractToInputs(a *API, u *account.UTXO, xpubs []chainkd.XPub) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
+       sourceID := bytom.Hash{
+               V0: u.SourceID.GetV0(),
+               V1: u.SourceID.GetV1(),
+               V2: u.SourceID.GetV2(),
+               V3: u.SourceID.GetV3(),
+       }
+
+       assetID := bytom.AssetID{
+               V0: u.AssetID.GetV0(),
+               V1: u.AssetID.GetV1(),
+               V2: u.AssetID.GetV2(),
+               V3: u.AssetID.GetV3(),
+       }
+
+       txInput := bytomtypes.NewSpendInput(nil, sourceID, assetID, u.Amount, u.SourcePos, u.ControlProgram)
+       sigInst := &mainchain.SigningInstruction{}
+       for _, xpub := range xpubs {
+               xpubsTmp := []chainkd.XPub{xpub}
+               sigInst.AddRawWitnessKeysWithoutPath(xpubsTmp, 1)
+       }
+       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
+}
index 11408df..1d98bfd 100644 (file)
@@ -62,3 +62,29 @@ func (a *API) getPeginAddress(ctx context.Context, ins struct {
                ClaimScript:      claimScript,
        })
 }
+
+func (a *API) getPeginContract(ctx context.Context, ins struct {
+       AccountID    string `json:"account_id"`
+       AccountAlias string `json:"account_alias"`
+}) Response {
+
+       accountID := ins.AccountID
+       if ins.AccountAlias != "" {
+               account, err := a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
+               if err != nil {
+                       return NewErrorResponse(err)
+               }
+
+               accountID = account.ID
+       }
+
+       peginContractPrograms, claimScript, err := a.wallet.AccountMgr.CreatePeginContractPrograms(accountID, false)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       return NewSuccessResponse(fundingResp{
+               MainchainAddress: peginContractPrograms,
+               ClaimScript:      claimScript,
+       })
+}
index 7ab8f4d..1c0f274 100644 (file)
@@ -1,37 +1,23 @@
 package api
 
 import (
-       "bytes"
        "context"
-       "crypto/hmac"
-       "crypto/sha256"
        "encoding/json"
        "math"
-       "strconv"
        "strings"
        "time"
 
        log "github.com/sirupsen/logrus"
 
        "github.com/vapor/account"
-       "github.com/vapor/blockchain/signers"
        "github.com/vapor/blockchain/txbuilder"
-       "github.com/vapor/blockchain/txbuilder/mainchain"
-       "github.com/vapor/common"
        "github.com/vapor/consensus"
        "github.com/vapor/consensus/segwit"
-       "github.com/vapor/crypto/ed25519/chainkd"
-       "github.com/vapor/crypto/sha3pool"
-       chainjson "github.com/vapor/encoding/json"
        "github.com/vapor/errors"
        "github.com/vapor/math/checked"
        "github.com/vapor/net/http/reqid"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
-       "github.com/vapor/protocol/bc/types/bytom"
-       bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
-       "github.com/vapor/protocol/vm/vmutil"
-       "github.com/vapor/util"
 )
 
 var (
@@ -351,464 +337,3 @@ func (a *API) estimateTxGas(ctx context.Context, in struct {
        }
        return NewSuccessResponse(txGasResp)
 }
-
-func getPeginTxnOutputIndex(rawTx bytomtypes.Tx, controlProg []byte) int {
-       for index, output := range rawTx.Outputs {
-               if bytes.Equal(output.ControlProgram, controlProg) {
-                       return index
-               }
-       }
-       return 0
-}
-
-func toHash(hexBytes []chainjson.HexBytes) (hashs []*bytom.Hash) {
-       for _, data := range hexBytes {
-               b32 := [32]byte{}
-               copy(b32[:], data)
-               res := bytom.NewHash(b32)
-               hashs = append(hashs, &res)
-       }
-       return
-}
-
-func (a *API) claimPeginTx(ctx context.Context, ins struct {
-       Password     string                 `json:"password"`
-       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
-       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
-       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
-       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
-       Flags        []uint32               `json:"flags"`
-       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
-       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
-}) Response {
-       tmpl, err := a.createrawpegin(ctx, ins)
-       if err != nil {
-               log.WithField("build err", err).Error("fail on createrawpegin.")
-               return NewErrorResponse(err)
-       }
-       // 交易签名
-       if err := txbuilder.Sign(ctx, tmpl, ins.Password, a.PseudohsmSignTemplate); err != nil {
-               log.WithField("build err", err).Error("fail on sign transaction.")
-               return NewErrorResponse(err)
-       }
-
-       // submit
-       if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
-               return NewErrorResponse(err)
-       }
-
-       log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
-       return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
-}
-
-// GetMerkleBlockResp is resp struct for GetTxOutProof API
-type GetMerkleBlock struct {
-       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
-       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
-       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
-       Flags        []uint32               `json:"flags"`
-       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
-}
-
-func (a *API) createrawpegin(ctx context.Context, ins struct {
-       Password     string                 `json:"password"`
-       RawTx        bytomtypes.Tx          `json:"raw_transaction"`
-       BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
-       TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
-       StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
-       Flags        []uint32               `json:"flags"`
-       MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
-       ClaimScript  chainjson.HexBytes     `json:"claim_script"`
-}) (*txbuilder.Template, error) {
-       // proof验证
-       var flags []uint8
-       for flag := range ins.Flags {
-               flags = append(flags, uint8(flag))
-       }
-       txHashes := toHash(ins.TxHashes)
-       matchedTxIDs := toHash(ins.MatchedTxIDs)
-       if !bytomtypes.ValidateTxMerkleTreeProof(txHashes, flags, matchedTxIDs, ins.BlockHeader.BlockCommitment.TransactionsMerkleRoot) {
-               return nil, errors.New("Merkleblock validation failed")
-       }
-       // CheckBytomProof
-       //difficulty.CheckBytomProofOfWork(ins.BlockHeader.Hash(), ins.BlockHeader)
-       // 增加spv验证以及连接主链api查询交易的确认数
-       if util.ValidatePegin {
-               if err := util.IsConfirmedBytomBlock(ins.BlockHeader.Height, consensus.ActiveNetParams.PeginMinDepth); err != nil {
-                       return nil, err
-               }
-       }
-       // 找出与claim script有关联的交易的输出
-       var claimScript []byte
-       nOut := len(ins.RawTx.Outputs)
-       if ins.ClaimScript == nil {
-               // 遍历寻找与交易输出有关的claim script
-               cps, err := a.wallet.AccountMgr.ListControlProgram()
-               if err != nil {
-                       return nil, err
-               }
-
-               for _, cp := range cps {
-                       _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
-                       if controlProg == nil {
-                               continue
-                       }
-                       // 获取交易的输出
-                       nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
-                       if nOut != len(ins.RawTx.Outputs) {
-                               claimScript = cp.ControlProgram
-                       }
-               }
-       } else {
-               claimScript = ins.ClaimScript
-               _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
-               // 获取交易的输出
-               nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
-       }
-       if nOut == len(ins.RawTx.Outputs) {
-               return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
-       }
-
-       // 根据ClaimScript 获取account id
-       var hash [32]byte
-       sha3pool.Sum256(hash[:], claimScript)
-       data := a.wallet.DB.Get(account.ContractKey(hash))
-       if data == nil {
-               return nil, errors.New("Failed to find control program through claim script")
-       }
-
-       cp := &account.CtrlProgram{}
-       if err := json.Unmarshal(data, cp); err != nil {
-               return nil, errors.New("Failed on unmarshal control program")
-       }
-
-       // 构造交易
-       // 用输出作为交易输入 生成新的交易
-       builder := txbuilder.NewBuilder(time.Now())
-       // TODO 根据raw tx生成一个utxo
-       //txInput := types.NewClaimInputInput(nil, *ins.RawTx.Outputs[nOut].AssetId, ins.RawTx.Outputs[nOut].Amount, cp.ControlProgram)
-       assetId := bc.AssetID{}
-       assetId.V0 = ins.RawTx.Outputs[nOut].AssetId.GetV0()
-       assetId.V1 = ins.RawTx.Outputs[nOut].AssetId.GetV1()
-       assetId.V2 = ins.RawTx.Outputs[nOut].AssetId.GetV2()
-       assetId.V3 = ins.RawTx.Outputs[nOut].AssetId.GetV3()
-
-       sourceID := bc.Hash{}
-       sourceID.V0 = ins.RawTx.OutputID(nOut).GetV0()
-       sourceID.V1 = ins.RawTx.OutputID(nOut).GetV1()
-       sourceID.V2 = ins.RawTx.OutputID(nOut).GetV2()
-       sourceID.V3 = ins.RawTx.OutputID(nOut).GetV3()
-       outputAccount := ins.RawTx.Outputs[nOut].Amount
-
-       txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
-       if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
-               return nil, err
-       }
-       program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
-       if err != nil {
-               return nil, err
-       }
-
-       if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
-               return nil, err
-       }
-
-       tmpl, txData, err := builder.Build()
-       if err != nil {
-               return nil, err
-       }
-
-       // todo把一些主链的信息加到交易的stack中
-       var stack [][]byte
-
-       //amount
-       amount := strconv.FormatUint(ins.RawTx.Outputs[nOut].Amount, 10)
-       stack = append(stack, []byte(amount))
-       // 主链的gennesisBlockHash
-       stack = append(stack, []byte(consensus.ActiveNetParams.ParentGenesisBlockHash))
-       // claim script
-       stack = append(stack, claimScript)
-       // raw tx
-       tx, _ := json.Marshal(ins.RawTx)
-       stack = append(stack, tx)
-       // proof
-       MerkleBLock := GetMerkleBlock{
-               BlockHeader:  ins.BlockHeader,
-               TxHashes:     ins.TxHashes,
-               StatusHashes: ins.StatusHashes,
-               Flags:        ins.Flags,
-               MatchedTxIDs: ins.MatchedTxIDs,
-       }
-       txOutProof, _ := json.Marshal(MerkleBLock)
-       stack = append(stack, txOutProof)
-
-       //      tmpl.Transaction.Inputs[0].Peginwitness = stack
-       txData.Inputs[0].Peginwitness = stack
-
-       //交易费估算
-       txGasResp, err := EstimateTxGas(*tmpl)
-       if err != nil {
-               return nil, err
-       }
-       txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
-       //重设置Transaction
-       tmpl.Transaction = types.NewTx(*txData)
-       return tmpl, nil
-}
-
-func (a *API) buildMainChainTx(ins struct {
-       Utxo           account.UTXO       `json:"utxo"`
-       Tx             types.Tx           `json:"raw_transaction"`
-       RootXPubs      []chainkd.XPub     `json:"root_xpubs"`
-       Alias          string             `json:"alias"`
-       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)
-       }
-       acc := &account.Account{}
-       var err error
-       if acc, err = a.wallet.AccountMgr.FindByAlias(ins.Alias); err != nil {
-               acc, err = a.wallet.AccountMgr.Create(xpubs, len(xpubs), ins.Alias, signers.BIP0044)
-               if err != nil {
-                       return NewErrorResponse(err)
-               }
-       }
-       ins.Utxo.ControlProgramIndex = acc.Signer.KeyIndex
-
-       txInput, sigInst, err := utxoToInputs(acc.Signer, &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 := bytom.AssetID{
-                       V0: output.Source.Value.AssetId.GetV0(),
-                       V1: output.Source.Value.AssetId.GetV1(),
-                       V2: output.Source.Value.AssetId.GetV2(),
-                       V3: output.Source.Value.AssetId.GetV3(),
-               }
-               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
-               assetID := bytom.AssetID{
-                       V0: u.AssetID.GetV0(),
-                       V1: u.AssetID.GetV1(),
-                       V2: u.AssetID.GetV2(),
-                       V3: u.AssetID.GetV3(),
-               }
-               out := bytomtypes.NewTxOutput(assetID, changeAmount, ins.Utxo.ControlProgram)
-               builder.AddOutput(out)
-       }
-
-       tmpl, tx, err := builder.Build()
-       if err != nil {
-               return NewErrorResponse(err)
-       }
-       //交易费估算
-       txGasResp, err := EstimateTxGasForMainchain(*tmpl)
-       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
-}
-
-// UtxoToInputs convert an utxo to the txinput
-func utxoToInputs(signer *signers.Signer, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
-       sourceID := bytom.Hash{
-               V0: u.SourceID.GetV0(),
-               V1: u.SourceID.GetV1(),
-               V2: u.SourceID.GetV2(),
-               V3: u.SourceID.GetV3(),
-       }
-
-       assetID := bytom.AssetID{
-               V0: u.AssetID.GetV0(),
-               V1: u.AssetID.GetV1(),
-               V2: u.AssetID.GetV2(),
-               V3: u.AssetID.GetV3(),
-       }
-
-       txInput := bytomtypes.NewSpendInput(nil, sourceID, assetID, u.Amount, u.SourcePos, u.ControlProgram)
-       sigInst := &mainchain.SigningInstruction{}
-       if signer == nil {
-               return txInput, sigInst, nil
-       }
-
-       // TODO  u.Change看怎么填写
-       path, _ := signers.Path(signer, signers.AccountKeySpace, u.Change, u.ControlProgramIndex)
-       if u.Address == "" {
-               sigInst.AddWitnessKeys(signer.XPubs, path, signer.Quorum)
-               return txInput, sigInst, nil
-       }
-
-       address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
-       if err != nil {
-               return nil, nil, err
-       }
-
-       switch address.(type) {
-       case *common.AddressWitnessPubKeyHash:
-               sigInst.AddRawWitnessKeys(signer.XPubs, path, signer.Quorum)
-               derivedXPubs := chainkd.DeriveXPubs(signer.XPubs, path)
-               derivedPK := derivedXPubs[0].PublicKey()
-               sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness([]byte(derivedPK)))
-
-       case *common.AddressWitnessScriptHash:
-               sigInst.AddRawWitnessKeys(signer.XPubs, path, signer.Quorum)
-               //path := signers.Path(signer, signers.AccountKeySpace, u.ControlProgramIndex)
-               //derivedXPubs := chainkd.DeriveXPubs(signer.XPubs, path)
-               derivedXPubs := signer.XPubs
-               derivedPKs := chainkd.XPubKeys(derivedXPubs)
-               script, err := vmutil.P2SPMultiSigProgram(derivedPKs, signer.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 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
-}
index 727af33..22e6179 100644 (file)
@@ -2,6 +2,7 @@ package mainchain
 
 import (
        "encoding/json"
+       "fmt"
 
        "github.com/vapor/crypto/ed25519/chainkd"
        chainjson "github.com/vapor/encoding/json"
@@ -32,10 +33,8 @@ func (sw *RawTxSigWitness) Sign(tpl *Template, index uint32, xprv chainkd.XPrv)
                        // Already have a signature for this key
                        continue
                }
-               path := make([][]byte, len(keyID.DerivationPath))
-               for i, p := range keyID.DerivationPath {
-                       path[i] = p
-               }
+               fmt.Println(keyID.XPub.String())
+               fmt.Println(xprv.XPub().String())
                if keyID.XPub.String() != xprv.XPub().String() {
                        continue
                }
index 944b70c..ff81b67 100644 (file)
@@ -12,15 +12,11 @@ import (
 // AddWitnessKeys adds a SignatureWitness with the given quorum and
 // list of keys derived by applying the derivation path to each of the
 // xpubs.
-func (si *SigningInstruction) AddWitnessKeys(xpubs []chainkd.XPub, path [][]byte, quorum int) {
-       hexPath := make([]chainjson.HexBytes, 0, len(path))
-       for _, p := range path {
-               hexPath = append(hexPath, p)
-       }
+func (si *SigningInstruction) AddWitnessKeys(xpubs []chainkd.XPub, quorum int) {
 
        keyIDs := make([]keyID, 0, len(xpubs))
        for _, xpub := range xpubs {
-               keyIDs = append(keyIDs, keyID{xpub, hexPath})
+               keyIDs = append(keyIDs, keyID{XPub: xpub})
        }
 
        sw := &SignatureWitness{
@@ -51,6 +47,20 @@ func (si *SigningInstruction) AddRawWitnessKeys(xpubs []chainkd.XPub, path [][]b
        si.WitnessComponents = append(si.WitnessComponents, sw)
 }
 
+func (si *SigningInstruction) AddRawWitnessKeysWithoutPath(xpubs []chainkd.XPub, quorum int) {
+
+       keyIDs := make([]keyID, 0, len(xpubs))
+       for _, xpub := range xpubs {
+               keyIDs = append(keyIDs, keyID{XPub: xpub})
+       }
+
+       sw := &RawTxSigWitness{
+               Quorum: quorum,
+               Keys:   keyIDs,
+       }
+       si.WitnessComponents = append(si.WitnessComponents, sw)
+}
+
 // SigningInstruction gives directions for signing inputs in a TxTemplate.
 type SigningInstruction struct {
        Position          uint32             `json:"position"`
index 61c51ae..7305022 100644 (file)
@@ -205,27 +205,29 @@ func initActiveNetParams(config *cfg.Config) {
        if !exist {
                cmn.Exit(cmn.Fmt("chain_id[%v] don't exist", config.ChainID))
        }
-       var federationRedeemXPubs []chainkd.XPub
-       if fedpegXPubs := strings.Split(config.Side.FedpegXPubs, ","); len(fedpegXPubs) > 0 {
+       if config.Side.FedpegXPubs != "" {
+               var federationRedeemXPubs []chainkd.XPub
+               fedpegXPubs := strings.Split(config.Side.FedpegXPubs, ",")
                for _, xpubStr := range fedpegXPubs {
                        var xpub chainkd.XPub
                        xpub.UnmarshalText([]byte(xpubStr))
                        federationRedeemXPubs = append(federationRedeemXPubs, xpub)
                }
+               consensus.ActiveNetParams.FedpegXPubs = federationRedeemXPubs
        }
 
-       var signBlockXPubs []chainkd.XPub
-       if xPubs := strings.Split(config.Side.SignBlockXPubs, ","); len(xPubs) > 0 {
+       if config.Side.SignBlockXPubs != "" {
+               var signBlockXPubs []chainkd.XPub
+               xPubs := strings.Split(config.Side.SignBlockXPubs, ",")
                for _, xpubStr := range xPubs {
                        var xpub chainkd.XPub
                        xpub.UnmarshalText([]byte(xpubStr))
                        signBlockXPubs = append(signBlockXPubs, xpub)
                }
+               consensus.ActiveNetParams.SignBlockXPubs = signBlockXPubs
        }
 
        consensus.ActiveNetParams.Signer = config.Signer
-       consensus.ActiveNetParams.FedpegXPubs = federationRedeemXPubs
-       consensus.ActiveNetParams.SignBlockXPubs = signBlockXPubs
        consensus.ActiveNetParams.PeginMinDepth = config.Side.PeginMinDepth
        consensus.ActiveNetParams.ParentGenesisBlockHash = config.Side.ParentGenesisBlockHash
 }
index aa540e4..17fa73b 100644 (file)
@@ -223,8 +223,7 @@ def send_to_mainchain():
         return json_contents(jsonify(code=-1, msg="get side raw transaction fail"))
 
     # 构建主链交易
-    body_json = '{"claim_script":"%s","raw_transaction": "%s","alias": "%s","control_program":"%s","root_xpubs":%s,%s}' % (claim_script,raw_transaction,alias,control_program,root_xpubs,utxo)
-    print body_json
+    body_json = '{"claim_script":"%s","raw_transaction": "%s","control_program":"%s","root_xpubs":%s,%s}' % (claim_script,raw_transaction,control_program,root_xpubs,utxo)
     response = connSide.request("/build-mainchain-tx",json.loads(body_json))
     resp_json = json.loads(response.text.encode('utf-8'))
     tmpl = ""
@@ -237,15 +236,12 @@ def send_to_mainchain():
         return json_contents(jsonify(code=-1, msg="build mainchain transaction fail"))
 
     # 签名
-    #xpubs = request.json['root_xpubs']
-    #xprvs = request.json['xprvs']
-    #for index in range(len(xpubs)):
     for key,value in key_pair.items():
-        #xprv = request.json['xprv'].encode('utf-8')
         body_json = '{"xprv": "%s","xpub":"%s","transaction":%s,"claim_script":"%s"}' % (key,value,tmpl,claim_script)
         response = connSide.request("/sign-with-key",json.loads(body_json))
         resp_json = json.loads(response.text.encode('utf-8'))
         if resp_json['status'] == 'success':
+            tmpl = json.dumps(resp_json['data']['transaction'])
             raw_transaction = resp_json['data']['transaction']['raw_transaction'].encode('utf-8')
         elif resp_json['status'] == 'fail':
             return json_contents(jsonify(code=-1, msg="sign-with-key: " + resp_json['msg']))
@@ -256,6 +252,7 @@ def send_to_mainchain():
     body_json = '{"raw_transaction": "%s"}' % (raw_transaction)
     response = connMain.request("/submit-transaction",json.loads(body_json))
     resp_json = json.loads(response.text.encode('utf-8'))
+    print resp_json
     if resp_json['status'] == 'success':
         return json_contents(jsonify(code=200, msg=resp_json['data']))
     elif resp_json['status'] == 'fail':
index 0865470..1000998 100644 (file)
@@ -4,7 +4,7 @@ if [ ! -f "key_pair" ];then
 ./vapor init --chain_id solonet -r "side_chain"
 nohup ./vapor node -r "side_chain" > /dev/null &
 sleep 30
-curl -s -X POST -d '{}' http://127.0.0.1:8888/create-key-pair > key_pair
+curl -s -X POST -d '{}' http://127.0.0.1:8888/create-key-pair >> key_pair
 ps -ef | grep vapor | grep -v grep | awk  '{print $2}' |xargs  kill -9
 rm -rf side_chain
 fi
@@ -31,7 +31,7 @@ python manager.py db upgrade
 touch install
 fi
 
-nohup python manager.py runserver -p 8080 -h 0.0.0.0 > /dev/null &
+nohup python manager.py runserver -p 8000 -h 0.0.0.0 > /dev/null &
 sleep 30
 cd web
-nohup python -m SimpleHTTPServer 80 > /dev/null &
+nohup python -m SimpleHTTPServer 8080 > /dev/null &