From: mars Date: Thu, 29 Nov 2018 03:09:07 +0000 (+0800) Subject: Modify the dependency on path X-Git-Tag: v1.0.5~223^2~2^2 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=commitdiff_plain;h=8964bcf0ffc12618f95c54f6677fcf45c7919534 Modify the dependency on path --- diff --git a/account/accounts.go b/account/accounts.go index eb6474a4..01041835 100644 --- a/account/accounts.go +++ b/account/accounts.go @@ -4,6 +4,7 @@ package account import ( "crypto/hmac" "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" "reflect" @@ -831,7 +832,7 @@ func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) { } var lockWith2of3KeysFmt = ` -contract LockWith3Keys(%s) locks amount of asset { +contract LockWith2of3Keys(%s) locks amount of asset { clause unlockWith2Sigs(%s) { verify checkTxMultiSig(%s) unlock amount of asset @@ -839,6 +840,22 @@ contract LockWith3Keys(%s) locks 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) @@ -847,7 +864,7 @@ func (m *Manager) CreatePeginContractAddress(accountID string, change bool) (str } claimScript := claimCtrlProg.ControlProgram - peginContractPrograms, err := m.getPeginContractPrograms(claimScript) + peginContractPrograms, err := m.GetPeginContractPrograms(claimScript) if err != nil { return "", nil, err } @@ -865,7 +882,7 @@ func (m *Manager) CreatePeginContractAddress(accountID string, change bool) (str func (m *Manager) GetPeginContractControlPrograms(claimScript []byte) (string, []byte) { - peginContractPrograms, err := m.getPeginContractPrograms(claimScript) + peginContractPrograms, err := m.GetPeginContractPrograms(claimScript) if err != nil { return "", nil } @@ -887,11 +904,10 @@ func (m *Manager) GetPeginContractControlPrograms(claimScript []byte) (string, [ return address.EncodeAddress(), program } -func (m *Manager) getPeginContractPrograms(claimScript []byte) ([]byte, error) { +func (m *Manager) GetPeginContractPrograms(claimScript []byte) ([]byte, error) { pubkeys := getNewXpub(consensus.ActiveNetParams.FedpegXPubs, claimScript) num := len(pubkeys) - fmt.Println(pubkeys) if num == 0 { return nil, errors.New("Fedpeg's XPubs is empty") } @@ -911,7 +927,6 @@ func (m *Manager) getPeginContractPrograms(claimScript []byte) ([]byte, error) { params += ": PublicKey" checkParams += "],[" - fmt.Println(params) signNum := getNumberOfSignaturesRequired(pubkeys) for index := 0; index < signNum; index++ { param := fmt.Sprintf("sig%d", index+1) @@ -927,7 +942,6 @@ func (m *Manager) getPeginContractPrograms(claimScript []byte) ([]byte, error) { checkParams += "]" lockWith2of3Keys := fmt.Sprintf(lockWith2of3KeysFmt, params, unlockParams, checkParams) - fmt.Println(lockWith2of3Keys) r := strings.NewReader(lockWith2of3Keys) compiled, err := compiler.Compile(r) if err != nil { @@ -991,9 +1005,6 @@ func convertArguments(contract *compiler.Contract, args []ed25519.PublicKey) ([] var argument compiler.ContractArg switch p.Type { case "PublicKey": - if len(args[i]) != 64 { - return nil, errors.New("mismatch length for Asset/Hash/PublicKey argument") - } argument.S = (*chainjson.HexBytes)(&args[i]) default: return nil, errors.New("Contract parameter type error") diff --git a/api/api.go b/api/api.go index bea80e6a..d3a57906 100644 --- a/api/api.go +++ b/api/api.go @@ -248,7 +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-address", jsonHandler(a.getAddressForPeginContract)) + 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 index 00000000..413968da --- /dev/null +++ b/api/cliam_transact.go @@ -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 index 00000000..6a35d453 --- /dev/null +++ b/api/main_transact.go @@ -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 +} diff --git a/api/receivers.go b/api/receivers.go index b5da963d..1d98bfd4 100644 --- a/api/receivers.go +++ b/api/receivers.go @@ -63,7 +63,7 @@ func (a *API) getPeginAddress(ctx context.Context, ins struct { }) } -func (a *API) getAddressForPeginContract(ctx context.Context, ins struct { +func (a *API) getPeginContract(ctx context.Context, ins struct { AccountID string `json:"account_id"` AccountAlias string `json:"account_alias"` }) Response { @@ -78,13 +78,13 @@ func (a *API) getAddressForPeginContract(ctx context.Context, ins struct { accountID = account.ID } - mainchainAddress, claimScript, err := a.wallet.AccountMgr.CreatePeginContractAddress(accountID, false) + peginContractPrograms, claimScript, err := a.wallet.AccountMgr.CreatePeginContractPrograms(accountID, false) if err != nil { return NewErrorResponse(err) } return NewSuccessResponse(fundingResp{ - MainchainAddress: mainchainAddress, + MainchainAddress: peginContractPrograms, ClaimScript: claimScript, }) } diff --git a/api/transact.go b/api/transact.go index 7ab8f4da..1c0f2741 100644 --- a/api/transact.go +++ b/api/transact.go @@ -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 -} diff --git a/blockchain/txbuilder/mainchain/rawtxsig_witness.go b/blockchain/txbuilder/mainchain/rawtxsig_witness.go index 727af336..22e61792 100644 --- a/blockchain/txbuilder/mainchain/rawtxsig_witness.go +++ b/blockchain/txbuilder/mainchain/rawtxsig_witness.go @@ -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 } diff --git a/blockchain/txbuilder/mainchain/signing_instruction.go b/blockchain/txbuilder/mainchain/signing_instruction.go index 944b70c1..ff81b67d 100644 --- a/blockchain/txbuilder/mainchain/signing_instruction.go +++ b/blockchain/txbuilder/mainchain/signing_instruction.go @@ -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"` diff --git a/node/node.go b/node/node.go index c3b20526..7305022a 100644 --- a/node/node.go +++ b/node/node.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "net" "net/http" _ "net/http/pprof" @@ -219,7 +218,6 @@ func initActiveNetParams(config *cfg.Config) { if config.Side.SignBlockXPubs != "" { var signBlockXPubs []chainkd.XPub - fmt.Println(signBlockXPubs) xPubs := strings.Split(config.Side.SignBlockXPubs, ",") for _, xpubStr := range xPubs { var xpub chainkd.XPub diff --git a/tools/side_chain_tool/app/api/transaction.py b/tools/side_chain_tool/app/api/transaction.py index aa540e41..17fa73b4 100644 --- a/tools/side_chain_tool/app/api/transaction.py +++ b/tools/side_chain_tool/app/api/transaction.py @@ -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': diff --git a/tools/side_chain_tool/sidechain.sh b/tools/side_chain_tool/sidechain.sh index 0865470e..1000998b 100644 --- a/tools/side_chain_tool/sidechain.sh +++ b/tools/side_chain_tool/sidechain.sh @@ -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 &