github.com/turingchain2020/turingchain@v1.1.21/account/account.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 account 实现turingchain 区块链资产操作 6 package account 7 8 //package for account manger 9 //1. load from db 10 //2. save to db 11 //3. KVSet 12 //4. Transfer 13 //5. Add 14 //6. Sub 15 //7. Account balance query 16 //8. gen a private key -> private key to address (bitcoin likes) 17 18 import ( 19 "strings" 20 21 "github.com/turingchain2020/turingchain/client" 22 "github.com/turingchain2020/turingchain/common" 23 "github.com/turingchain2020/turingchain/common/address" 24 dbm "github.com/turingchain2020/turingchain/common/db" 25 log "github.com/turingchain2020/turingchain/common/log/log15" 26 "github.com/turingchain2020/turingchain/types" 27 "github.com/golang/protobuf/proto" 28 ) 29 30 var alog = log.New("module", "account") 31 32 // DB for account 33 type DB struct { 34 db dbm.KV 35 accountKeyPerfix []byte 36 execAccountKeyPerfix []byte 37 execer string 38 symbol string 39 accountKeyBuffer []byte 40 cfg *types.TuringchainConfig 41 } 42 43 // NewCoinsAccount 新建账户 44 func NewCoinsAccount(cfg *types.TuringchainConfig) *DB { 45 prefix := "mavl-coins-" + cfg.GetCoinSymbol() + "-" 46 return newAccountDB(cfg, prefix) 47 } 48 49 // NewAccountDB 新建DB账户 50 func NewAccountDB(cfg *types.TuringchainConfig, execer string, symbol string, db dbm.KV) (*DB, error) { 51 //如果execer 和 symbol 中存在 "-", 那么创建失败 52 if strings.ContainsRune(execer, '-') { 53 return nil, types.ErrExecNameNotAllow 54 } 55 if strings.ContainsRune(symbol, '-') { 56 return nil, types.ErrSymbolNameNotAllow 57 } 58 accDB := newAccountDB(cfg, symbolPrefix(execer, symbol)) 59 accDB.execer = execer 60 accDB.symbol = symbol 61 accDB.SetDB(db) 62 return accDB, nil 63 } 64 65 func newAccountDB(cfg *types.TuringchainConfig, prefix string) *DB { 66 acc := &DB{cfg: cfg} 67 acc.accountKeyPerfix = []byte(prefix) 68 acc.accountKeyBuffer = make([]byte, 0, len(acc.accountKeyPerfix)+64) 69 acc.accountKeyBuffer = append(acc.accountKeyBuffer, acc.accountKeyPerfix...) 70 acc.execAccountKeyPerfix = append([]byte(prefix), []byte("exec-")...) 71 //alog.Warn("NewAccountDB", "prefix", prefix, "key1", string(acc.accountKeyPerfix), "key2", string(acc.execAccountKeyPerfix)) 72 return acc 73 } 74 75 // SetDB set db 76 func (acc *DB) SetDB(db dbm.KV) *DB { 77 acc.db = db 78 return acc 79 } 80 81 func (acc *DB) accountReadKey(addr string) []byte { 82 acc.accountKeyBuffer = acc.accountKeyBuffer[0:len(acc.accountKeyPerfix)] 83 acc.accountKeyBuffer = append(acc.accountKeyBuffer, []byte(addr)...) 84 return acc.accountKeyBuffer 85 } 86 87 // LoadAccount 根据地址载入账户 88 func (acc *DB) LoadAccount(addr string) *types.Account { 89 value, err := acc.db.Get(acc.accountReadKey(addr)) 90 if err != nil { 91 return &types.Account{Addr: addr} 92 } 93 var acc1 types.Account 94 err = types.Decode(value, &acc1) 95 if err != nil { 96 panic(err) //数据库已经损坏 97 } 98 return &acc1 99 } 100 101 // CheckTransfer 检查交易 102 func (acc *DB) CheckTransfer(from, to string, amount int64) error { 103 if !types.CheckAmount(amount) { 104 return types.ErrAmount 105 } 106 accFrom := acc.LoadAccount(from) 107 b := accFrom.GetBalance() - amount 108 if b < 0 { 109 return types.ErrNoBalance 110 } 111 return nil 112 } 113 114 // Transfer 执行交易 115 func (acc *DB) Transfer(from, to string, amount int64) (*types.Receipt, error) { 116 if !types.CheckAmount(amount) { 117 return nil, types.ErrAmount 118 } 119 accFrom := acc.LoadAccount(from) 120 accTo := acc.LoadAccount(to) 121 if accFrom.Addr == accTo.Addr { 122 return nil, types.ErrSendSameToRecv 123 } 124 if accFrom.GetBalance()-amount >= 0 { 125 copyfrom := *accFrom 126 copyto := *accTo 127 128 accFrom.Balance = accFrom.GetBalance() - amount 129 accTo.Balance = accTo.GetBalance() + amount 130 131 receiptBalanceFrom := &types.ReceiptAccountTransfer{ 132 Prev: ©from, 133 Current: accFrom, 134 } 135 receiptBalanceTo := &types.ReceiptAccountTransfer{ 136 Prev: ©to, 137 Current: accTo, 138 } 139 fromkv := acc.GetKVSet(accFrom) 140 tokv := acc.GetKVSet(accTo) 141 acc.SaveKVSet(fromkv) 142 acc.SaveKVSet(tokv) 143 return acc.transferReceipt(fromkv, tokv, receiptBalanceFrom, receiptBalanceTo), nil 144 } 145 146 return nil, types.ErrNoBalance 147 } 148 149 func (acc *DB) depositBalance(execaddr string, amount int64) (*types.Receipt, error) { 150 if !types.CheckAmount(amount) { 151 return nil, types.ErrAmount 152 } 153 acc1 := acc.LoadAccount(execaddr) 154 copyacc := *acc1 155 acc1.Balance += amount 156 receiptBalance := &types.ReceiptAccountTransfer{ 157 Prev: ©acc, 158 Current: acc1, 159 } 160 kv := acc.GetKVSet(acc1) 161 acc.SaveKVSet(kv) 162 log1 := &types.ReceiptLog{ 163 Ty: int32(types.TyLogDeposit), 164 Log: types.Encode(receiptBalance), 165 } 166 return &types.Receipt{ 167 Ty: types.ExecOk, 168 KV: kv, 169 Logs: []*types.ReceiptLog{log1}, 170 }, nil 171 } 172 173 func (acc *DB) transferReceipt(fromkv, tokv []*types.KeyValue, receiptFrom, receiptTo proto.Message) *types.Receipt { 174 ty := int32(types.TyLogTransfer) 175 log1 := &types.ReceiptLog{ 176 Ty: ty, 177 Log: types.Encode(receiptFrom), 178 } 179 log2 := &types.ReceiptLog{ 180 Ty: ty, 181 Log: types.Encode(receiptTo), 182 } 183 kv := make([]*types.KeyValue, 0, len(fromkv)+len(tokv)) 184 kv = append(kv, fromkv...) 185 kv = append(kv, tokv...) 186 return &types.Receipt{ 187 Ty: types.ExecOk, 188 KV: kv, 189 Logs: []*types.ReceiptLog{log1, log2}, 190 } 191 } 192 193 // SaveAccount 保存账户到数据库 194 func (acc *DB) SaveAccount(acc1 *types.Account) { 195 set := acc.GetKVSet(acc1) 196 for i := 0; i < len(set); i++ { 197 err := acc.db.Set(set[i].GetKey(), set[i].Value) 198 if err != nil { 199 panic(err) 200 } 201 } 202 } 203 204 //SaveKVSet 保存Key Value set 205 func (acc *DB) SaveKVSet(set []*types.KeyValue) { 206 for i := 0; i < len(set); i++ { 207 err := acc.db.Set(set[i].GetKey(), set[i].Value) 208 if err != nil { 209 panic(err) 210 } 211 } 212 } 213 214 // GetKVSet 将账户数据转为数据库存储kv 215 func (acc *DB) GetKVSet(acc1 *types.Account) (kvset []*types.KeyValue) { 216 value := types.Encode(acc1) 217 kvset = make([]*types.KeyValue, 1) 218 kvset[0] = &types.KeyValue{ 219 Key: acc.AccountKey(acc1.Addr), 220 Value: value, 221 } 222 return kvset 223 } 224 225 // LoadAccounts 从stateDB中载入若干账户 226 // TODO:使用API的方式访问,暂时与LoadAccounts()共存,后续将删除LoadAccounts() 227 func (acc *DB) LoadAccounts(api client.QueueProtocolAPI, addrs []string) (accs []*types.Account, err error) { 228 header, err := api.GetLastHeader() 229 if err != nil { 230 return nil, err 231 } 232 return acc.loadAccountsHistory(api, addrs, header.GetStateHash()) 233 } 234 235 // LoadAccountsDB 载入账户 236 func (acc *DB) LoadAccountsDB(addrs []string) (accs []*types.Account, err error) { 237 for i := 0; i < len(addrs); i++ { 238 acc1 := acc.LoadAccount(addrs[i]) 239 accs = append(accs, acc1) 240 } 241 return accs, nil 242 } 243 244 // AccountKey return the key of address in DB 245 func (acc *DB) AccountKey(address string) (key []byte) { 246 key = make([]byte, 0, len(acc.accountKeyPerfix)+len(address)) 247 key = append(key, acc.accountKeyPerfix...) 248 key = append(key, []byte(address)...) 249 return key 250 } 251 252 func symbolPrefix(execer string, symbol string) string { 253 return "mavl-" + execer + "-" + symbol + "-" 254 } 255 256 func symbolExecPrefix(execer string, symbol string) string { 257 return "mavl-" + execer + "-" + symbol + "-exec" 258 } 259 260 // GetTotalCoins 获取代币总量 261 func (acc *DB) GetTotalCoins(api client.QueueProtocolAPI, in *types.ReqGetTotalCoins) (reply *types.ReplyGetTotalCoins, err error) { 262 req := types.IterateRangeByStateHash{} 263 req.StateHash = in.StateHash 264 req.Count = in.Count 265 start := symbolPrefix(in.Execer, in.Symbol) 266 end := symbolExecPrefix(in.Execer, in.Symbol) 267 if in.StartKey == nil { 268 req.Start = []byte(start) 269 } else { 270 req.Start = in.StartKey 271 } 272 req.End = []byte(end) 273 return api.StoreGetTotalCoins(&req) 274 } 275 276 func (acc *DB) loadAccountsHistory(api client.QueueProtocolAPI, addrs []string, stateHash []byte) (accs []*types.Account, err error) { 277 get := types.StoreGet{StateHash: stateHash} 278 for i := 0; i < len(addrs); i++ { 279 get.Keys = append(get.Keys, acc.AccountKey(addrs[i])) 280 } 281 282 values, err := api.StoreGet(&get) 283 if err != nil { 284 return nil, err 285 } 286 287 for i := 0; i < len(values.Values); i++ { 288 value := values.Values[i] 289 if value == nil { 290 accs = append(accs, &types.Account{Addr: addrs[i]}) 291 } else { 292 var acc types.Account 293 err := types.Decode(value, &acc) 294 if err != nil { 295 return nil, err 296 } 297 accs = append(accs, &acc) 298 } 299 } 300 return accs, nil 301 } 302 303 // GetBalance 获取某个状态下账户余额 304 func (acc *DB) GetBalance(api client.QueueProtocolAPI, in *types.ReqBalance) ([]*types.Account, error) { 305 // load account 306 cfg := api.GetConfig() 307 if in.AssetExec == string(cfg.GetParaExec([]byte(in.Execer))) || "" == in.Execer { 308 addrs := in.GetAddresses() 309 var exaddrs []string 310 for _, addr := range addrs { 311 if err := address.CheckAddress(addr); err != nil { 312 addr = address.ExecAddress(addr) 313 } 314 exaddrs = append(exaddrs, addr) 315 } 316 var accounts []*types.Account 317 var err error 318 if len(in.StateHash) == 0 { 319 accounts, err = acc.LoadAccounts(api, exaddrs) 320 if err != nil { 321 log.Error("GetBalance", "err", err.Error()) 322 return nil, err 323 } 324 } else { 325 hash, err := common.FromHex(in.StateHash) 326 if err != nil { 327 return nil, err 328 } 329 accounts, err = acc.loadAccountsHistory(api, exaddrs, hash) 330 if err != nil { 331 log.Error("GetBalance", "err", err.Error()) 332 return nil, err 333 } 334 } 335 return accounts, nil 336 } 337 338 // load exec account 339 execaddress := address.ExecAddress(in.GetExecer()) 340 addrs := in.GetAddresses() 341 var accounts []*types.Account 342 for _, addr := range addrs { 343 var account *types.Account 344 var err error 345 if len(in.StateHash) == 0 { 346 account, err = acc.LoadExecAccountQueue(api, addr, execaddress) 347 if err != nil { 348 log.Error("GetBalance", "err", err.Error()) 349 continue 350 } 351 } else { 352 hash, err := common.FromHex(in.StateHash) 353 if err != nil { 354 return nil, err 355 } 356 account, err = acc.LoadExecAccountHistoryQueue(api, addr, execaddress, hash) 357 if err != nil { 358 log.Error("GetBalance", "err", err.Error()) 359 continue 360 } 361 } 362 accounts = append(accounts, account) 363 } 364 return accounts, nil 365 } 366 367 // GetExecBalance 通过account模块获取地址账户在合约中的余额 368 func (acc *DB) GetExecBalance(api client.QueueProtocolAPI, in *types.ReqGetExecBalance) (reply *types.ReplyGetExecBalance, err error) { 369 req := types.StoreList{} 370 req.StateHash = in.StateHash 371 372 prefix := symbolExecPrefix(in.Execer, in.Symbol) 373 if len(in.ExecAddr) > 0 { 374 prefix = prefix + "-" + string(in.ExecAddr) + ":" 375 } else { 376 prefix = prefix + "-" 377 } 378 379 req.Start = []byte(prefix) 380 req.End = genPrefixEdge(req.Start) 381 req.Suffix = in.Addr 382 req.Mode = 2 //1:为[start,end)模式,按前缀或者范围进行查找。2:为prefix + suffix遍历模式,先按前缀查找,再判断后缀是否满足条件。 383 req.Count = in.Count 384 385 if len(in.NextKey) > 0 { 386 req.Start = in.NextKey 387 } 388 389 reply = &types.ReplyGetExecBalance{} 390 //log.Info("DB.GetExecBalance", "hash", common.ToHex(req.StateHash), "Prefix", string(req.Start), "End", string(req.End), "Addr", string(req.Suffix)) 391 392 res, err := api.StoreList(&req) 393 if err != nil { 394 err = types.ErrTypeAsset 395 return nil, err 396 } 397 398 for i := 0; i < len(res.Keys); i++ { 399 strKey := string(res.Keys[i]) 400 log.Info("DB.GetExecBalance process one record", "key", strKey) 401 if !strings.HasPrefix(strKey, prefix) { 402 log.Error("accountDB.GetExecBalance key does not match prefix", "key", strKey, "prefix", prefix) 403 return nil, types.ErrTypeAsset 404 } 405 //如果prefix形如:mavl-coins-trc-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp: ,则是查找addr在一个合约地址上的余额,找到一个值即可结束。 406 if strings.HasSuffix(prefix, ":") { 407 addr := strKey[len(prefix):] 408 execAddr := []byte(prefix[(len(prefix) - len(addr) - 1):(len(prefix) - 1)]) 409 log.Info("DB.GetExecBalance record for specific exec addr", "execAddr", string(execAddr), "addr", addr) 410 reply.AddItem(execAddr, res.Values[i]) 411 } else { 412 combinAddr := strKey[len(prefix):] 413 addrs := strings.Split(combinAddr, ":") 414 if 2 != len(addrs) { 415 log.Error("accountDB.GetExecBalance key does not contain exec-addr & addr", "key", strKey, "combinAddr", combinAddr) 416 return nil, types.ErrTypeAsset 417 } 418 //log.Info("DB.GetExecBalance", "execAddr", addrs[0], "addr", addrs[1]) 419 reply.AddItem([]byte(addrs[0]), res.Values[i]) 420 } 421 } 422 423 reply.NextKey = res.NextKey 424 425 return reply, nil 426 } 427 428 func genPrefixEdge(prefix []byte) (r []byte) { 429 for j := 0; j < len(prefix); j++ { 430 r = append(r, prefix[j]) 431 } 432 433 i := len(prefix) - 1 434 for i >= 0 { 435 if r[i] < 0xff { 436 r[i]++ 437 break 438 } else { 439 i-- 440 } 441 } 442 443 return r 444 } 445 446 // Mint 铸币 447 func (acc *DB) Mint(addr string, amount int64) (*types.Receipt, error) { 448 if !types.CheckAmount(amount) { 449 return nil, types.ErrAmount 450 } 451 452 accTo := acc.LoadAccount(addr) 453 balance, err := safeAdd(accTo.Balance, amount) 454 if err != nil { 455 return nil, err 456 } 457 458 copyAcc := *accTo 459 accTo.Balance = balance 460 461 receipt := &types.ReceiptAccountMint{ 462 Prev: ©Acc, 463 Current: accTo, 464 } 465 kv := acc.GetKVSet(accTo) 466 acc.SaveKVSet(kv) 467 return acc.mintReceipt(kv, receipt), nil 468 } 469 470 func (acc *DB) mintReceipt(kv []*types.KeyValue, receipt proto.Message) *types.Receipt { 471 ty := int32(types.TyLogMint) 472 log1 := &types.ReceiptLog{ 473 Ty: ty, 474 Log: types.Encode(receipt), 475 } 476 477 return &types.Receipt{ 478 Ty: types.ExecOk, 479 KV: kv, 480 Logs: []*types.ReceiptLog{log1}, 481 } 482 } 483 484 // Burn 然收 485 func (acc *DB) Burn(addr string, amount int64) (*types.Receipt, error) { 486 if !types.CheckAmount(amount) { 487 return nil, types.ErrAmount 488 } 489 490 accTo := acc.LoadAccount(addr) 491 if accTo.Balance < amount { 492 return nil, types.ErrNoBalance 493 } 494 495 copyAcc := *accTo 496 accTo.Balance = accTo.Balance - amount 497 498 receipt := &types.ReceiptAccountBurn{ 499 Prev: ©Acc, 500 Current: accTo, 501 } 502 kv := acc.GetKVSet(accTo) 503 acc.SaveKVSet(kv) 504 return acc.burnReceipt(kv, receipt), nil 505 } 506 507 func (acc *DB) burnReceipt(kv []*types.KeyValue, receipt proto.Message) *types.Receipt { 508 ty := int32(types.TyLogBurn) 509 log1 := &types.ReceiptLog{ 510 Ty: ty, 511 Log: types.Encode(receipt), 512 } 513 514 return &types.Receipt{ 515 Ty: types.ExecOk, 516 KV: kv, 517 Logs: []*types.ReceiptLog{log1}, 518 } 519 }