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:    &copyfrom,
   133  			Current: accFrom,
   134  		}
   135  		receiptBalanceTo := &types.ReceiptAccountTransfer{
   136  			Prev:    &copyto,
   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:    &copyacc,
   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:    &copyAcc,
   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:    &copyAcc,
   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  }