github.com/turingchain2020/turingchain@v1.1.21/wallet/sendtx.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package wallet
     6  
     7  import (
     8  	"encoding/hex"
     9  	"errors"
    10  	"math/rand"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/turingchain2020/turingchain/common/address"
    15  	"github.com/turingchain2020/turingchain/common/crypto"
    16  	cty "github.com/turingchain2020/turingchain/system/dapp/coins/types"
    17  	"github.com/turingchain2020/turingchain/types"
    18  )
    19  
    20  func init() {
    21  	rand.Seed(types.Now().UnixNano())
    22  }
    23  
    24  // GetBalance 根据地址和执行器类型获取对应的金额
    25  func (wallet *Wallet) GetBalance(addr string, execer string) (*types.Account, error) {
    26  	if !wallet.isInited() {
    27  		return nil, types.ErrNotInited
    28  	}
    29  	return wallet.getBalance(addr, execer)
    30  }
    31  
    32  func (wallet *Wallet) getBalance(addr string, execer string) (*types.Account, error) {
    33  	reqbalance := &types.ReqBalance{Addresses: []string{addr}, Execer: execer}
    34  	reply, err := wallet.queryBalance(reqbalance)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return reply[0], nil
    39  }
    40  
    41  // GetAllPrivKeys 获取所有私钥信息
    42  func (wallet *Wallet) GetAllPrivKeys() ([]crypto.PrivKey, error) {
    43  	if !wallet.isInited() {
    44  		return nil, types.ErrNotInited
    45  	}
    46  	wallet.mtx.Lock()
    47  	defer wallet.mtx.Unlock()
    48  
    49  	return wallet.getAllPrivKeys()
    50  }
    51  
    52  func (wallet *Wallet) getAllPrivKeys() ([]crypto.PrivKey, error) {
    53  	accounts, err := wallet.getWalletAccounts()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	ok, err := wallet.checkWalletStatus()
    59  	if !ok && err != types.ErrOnlyTicketUnLocked {
    60  		return nil, err
    61  	}
    62  	var privs []crypto.PrivKey
    63  	for _, acc := range accounts {
    64  		priv, err := wallet.getPrivKeyByAddr(acc.Addr)
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  		privs = append(privs, priv)
    69  	}
    70  	return privs, nil
    71  }
    72  
    73  // GetHeight 获取当前区块最新高度
    74  func (wallet *Wallet) GetHeight() int64 {
    75  	if !wallet.isInited() {
    76  		return 0
    77  	}
    78  	msg := wallet.client.NewMessage("blockchain", types.EventGetBlockHeight, nil)
    79  	err := wallet.client.Send(msg, true)
    80  	if err != nil {
    81  		return 0
    82  	}
    83  	replyHeight, err := wallet.client.Wait(msg)
    84  	h := replyHeight.GetData().(*types.ReplyBlockHeight).Height
    85  	walletlog.Debug("getheight = ", "height", h)
    86  	if err != nil {
    87  		return 0
    88  	}
    89  	return h
    90  }
    91  
    92  func (wallet *Wallet) sendTransactionWait(payload types.Message, execer []byte, priv crypto.PrivKey, to string) (err error) {
    93  	hash, err := wallet.sendTransaction(payload, execer, priv, to)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	txinfo := wallet.waitTx(hash)
    98  	if txinfo.Receipt.Ty != types.ExecOk {
    99  		return errors.New("sendTransactionWait error")
   100  	}
   101  	return nil
   102  }
   103  
   104  // SendTransaction 发送一笔交易
   105  func (wallet *Wallet) SendTransaction(payload types.Message, execer []byte, priv crypto.PrivKey, to string) (hash []byte, err error) {
   106  	if !wallet.isInited() {
   107  		return nil, types.ErrNotInited
   108  	}
   109  	wallet.mtx.Lock()
   110  	defer wallet.mtx.Unlock()
   111  
   112  	return wallet.sendTransaction(payload, execer, priv, to)
   113  }
   114  
   115  func (wallet *Wallet) sendTransaction(payload types.Message, execer []byte, priv crypto.PrivKey, to string) (hash []byte, err error) {
   116  	if to == "" {
   117  		to = address.ExecAddress(string(execer))
   118  	}
   119  	tx := &types.Transaction{Execer: execer, Payload: types.Encode(payload), Fee: wallet.minFee, To: to}
   120  	tx.Nonce = rand.Int63()
   121  	tx.ChainID = wallet.client.GetConfig().GetChainID()
   122  
   123  	proper, err := wallet.api.GetProperFee(nil)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	fee, err := tx.GetRealFee(proper.ProperFee)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	tx.Fee = fee
   132  	tx.SetExpire(wallet.client.GetConfig(), time.Second*120)
   133  	tx.Sign(int32(wallet.SignType), priv)
   134  	reply, err := wallet.sendTx(tx)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	if !reply.IsOk {
   139  		walletlog.Info("wallet sendTransaction", "err", string(reply.GetMsg()))
   140  		return nil, errors.New(string(reply.GetMsg()))
   141  	}
   142  	return tx.Hash(), nil
   143  }
   144  
   145  func (wallet *Wallet) sendTx(tx *types.Transaction) (*types.Reply, error) {
   146  	if wallet.client == nil {
   147  		panic("client not bind message queue.")
   148  	}
   149  	return wallet.api.SendTx(tx)
   150  }
   151  
   152  // WaitTx 等待交易确认
   153  func (wallet *Wallet) WaitTx(hash []byte) *types.TransactionDetail {
   154  	return wallet.waitTx(hash)
   155  }
   156  
   157  func (wallet *Wallet) waitTx(hash []byte) *types.TransactionDetail {
   158  	i := 0
   159  	for {
   160  		if atomic.LoadInt32(&wallet.isclosed) == 1 {
   161  			return nil
   162  		}
   163  		i++
   164  		if i%100 == 0 {
   165  			walletlog.Error("wait transaction timeout", "hash", hex.EncodeToString(hash))
   166  			return nil
   167  		}
   168  		res, err := wallet.queryTx(hash)
   169  		if err != nil {
   170  			time.Sleep(time.Second)
   171  		}
   172  		if res != nil {
   173  			return res
   174  		}
   175  	}
   176  }
   177  
   178  // WaitTxs 等待多个交易确认
   179  func (wallet *Wallet) WaitTxs(hashes [][]byte) (ret []*types.TransactionDetail) {
   180  	return wallet.waitTxs(hashes)
   181  }
   182  
   183  func (wallet *Wallet) waitTxs(hashes [][]byte) (ret []*types.TransactionDetail) {
   184  	for _, hash := range hashes {
   185  		result := wallet.waitTx(hash)
   186  		ret = append(ret, result)
   187  	}
   188  	return ret
   189  }
   190  
   191  func (wallet *Wallet) queryTx(hash []byte) (*types.TransactionDetail, error) {
   192  	msg := wallet.client.NewMessage("blockchain", types.EventQueryTx, &types.ReqHash{Hash: hash})
   193  	err := wallet.client.Send(msg, true)
   194  	if err != nil {
   195  		walletlog.Error("QueryTx", "Error", err.Error())
   196  		return nil, err
   197  	}
   198  	resp, err := wallet.client.Wait(msg)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	return resp.Data.(*types.TransactionDetail), nil
   203  }
   204  
   205  // SendToAddress 想合约地址转账
   206  func (wallet *Wallet) SendToAddress(priv crypto.PrivKey, addrto string, amount int64, note string, Istoken bool, tokenSymbol string) (*types.ReplyHash, error) {
   207  	wallet.mtx.Lock()
   208  	defer wallet.mtx.Unlock()
   209  	return wallet.sendToAddress(priv, addrto, amount, note, Istoken, tokenSymbol)
   210  }
   211  
   212  func (wallet *Wallet) createSendToAddress(addrto string, amount int64, note string, Istoken bool, tokenSymbol string) (*types.Transaction, error) {
   213  	var tx *types.Transaction
   214  	var isWithdraw = false
   215  	if amount < 0 {
   216  		amount = -amount
   217  		isWithdraw = true
   218  	}
   219  	create := &types.CreateTx{
   220  		To:          addrto,
   221  		Amount:      amount,
   222  		Note:        []byte(note),
   223  		IsWithdraw:  isWithdraw,
   224  		IsToken:     Istoken,
   225  		TokenSymbol: tokenSymbol,
   226  	}
   227  
   228  	exec := cty.CoinsX
   229  	//历史原因,token是作为系统合约的,但是改版后,token变成非系统合约
   230  	//这样的情况下,的方案是做一些特殊的处理
   231  	if create.IsToken {
   232  		exec = "token"
   233  	}
   234  	ety := types.LoadExecutorType(exec)
   235  	if ety == nil {
   236  		return nil, types.ErrActionNotSupport
   237  	}
   238  	tx, err := ety.AssertCreate(create)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	cfg := wallet.client.GetConfig()
   243  	tx.SetExpire(cfg, time.Second*120)
   244  	proper, err := wallet.api.GetProperFee(nil)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	fee, err := tx.GetRealFee(proper.ProperFee)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	tx.Fee = fee
   253  	if tx.To == "" {
   254  		tx.To = addrto
   255  	}
   256  	if len(tx.Execer) == 0 {
   257  		tx.Execer = []byte(exec)
   258  	}
   259  
   260  	if cfg.IsPara() {
   261  		tx.Execer = []byte(cfg.GetTitle() + string(tx.Execer))
   262  		tx.To = address.ExecAddress(string(tx.Execer))
   263  	}
   264  
   265  	tx.Nonce = rand.Int63()
   266  	tx.ChainID = cfg.GetChainID()
   267  
   268  	return tx, nil
   269  }
   270  
   271  func (wallet *Wallet) sendToAddress(priv crypto.PrivKey, addrto string, amount int64, note string, Istoken bool, tokenSymbol string) (*types.ReplyHash, error) {
   272  	tx, err := wallet.createSendToAddress(addrto, amount, note, Istoken, tokenSymbol)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  	tx.Sign(int32(wallet.SignType), priv)
   277  
   278  	reply, err := wallet.api.SendTx(tx)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	if !reply.GetIsOk() {
   283  		return nil, errors.New(string(reply.GetMsg()))
   284  	}
   285  	var hash types.ReplyHash
   286  	hash.Hash = tx.Hash()
   287  	return &hash, nil
   288  }
   289  
   290  func (wallet *Wallet) queryBalance(in *types.ReqBalance) ([]*types.Account, error) {
   291  
   292  	switch in.GetExecer() {
   293  	case "coins":
   294  		addrs := in.GetAddresses()
   295  		var exaddrs []string
   296  		for _, addr := range addrs {
   297  			if err := address.CheckAddress(addr); err != nil {
   298  				addr = address.ExecAddress(addr)
   299  			}
   300  			exaddrs = append(exaddrs, addr)
   301  		}
   302  		accounts, err := wallet.accountdb.LoadAccounts(wallet.api, exaddrs)
   303  		if err != nil {
   304  			walletlog.Error("GetBalance", "err", err.Error())
   305  			return nil, err
   306  		}
   307  		return accounts, nil
   308  	default:
   309  		execaddress := address.ExecAddress(in.GetExecer())
   310  		addrs := in.GetAddresses()
   311  		var accounts []*types.Account
   312  		for _, addr := range addrs {
   313  			acc, err := wallet.accountdb.LoadExecAccountQueue(wallet.api, addr, execaddress)
   314  			if err != nil {
   315  				walletlog.Error("GetBalance", "err", err.Error())
   316  				return nil, err
   317  			}
   318  			accounts = append(accounts, acc)
   319  		}
   320  		return accounts, nil
   321  	}
   322  }
   323  
   324  func (wallet *Wallet) getMinerColdAddr(addr string) ([]string, error) {
   325  	reqaddr := &types.ReqString{Data: addr}
   326  	//ticket和pos33执行器有对应的miner
   327  	consensus := wallet.client.GetConfig().GetModuleConfig().Consensus.Name
   328  	req := types.ChainExecutor{
   329  		Driver:   consensus,
   330  		FuncName: "MinerSourceList",
   331  		Param:    types.Encode(reqaddr),
   332  	}
   333  
   334  	msg := wallet.client.NewMessage("exec", types.EventBlockChainQuery, &req)
   335  	err := wallet.client.Send(msg, true)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  	resp, err := wallet.client.Wait(msg)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  	reply := resp.GetData().(types.Message).(*types.ReplyStrings)
   344  	return reply.Datas, nil
   345  }
   346  
   347  // IsCaughtUp 检测当前区块是否已经同步完成
   348  func (wallet *Wallet) IsCaughtUp() bool {
   349  	if !wallet.isInited() {
   350  		return false
   351  	}
   352  	if wallet.client == nil {
   353  		panic("wallet client not bind message queue.")
   354  	}
   355  	reply, err := wallet.api.IsSync()
   356  	if err != nil {
   357  		return false
   358  	}
   359  	return reply.IsOk
   360  }