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 }