package account
import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/hex"
"encoding/json"
+ "fmt"
"reflect"
"sort"
"strings"
"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"
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
}
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
+}
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))
--- /dev/null
+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
+}
--- /dev/null
+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
+}
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,
+ })
+}
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 (
}
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
-}
import (
"encoding/json"
+ "fmt"
"github.com/vapor/crypto/ed25519/chainkd"
chainjson "github.com/vapor/encoding/json"
// 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
}
// 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{
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"`
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
}
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 = ""
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']))
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':
./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
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 &