Commit c7dfe374 authored by harrylee's avatar harrylee Committed by vipwzw

update accountmanager

parent 7275013e
......@@ -85,7 +85,7 @@ func TestAccountManager(t *testing.T) {
}
// set config key
item := &types.ConfigItem{
Key: "mavl-manage-"+ConfNameActiveTime,
Key: "mavl-manage-" + ConfNameActiveTime,
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{"10"}},
},
......@@ -93,7 +93,7 @@ func TestAccountManager(t *testing.T) {
stateDB.Set([]byte(item.Key), types.Encode(item))
item2 := &types.ConfigItem{
Key: "mavl-manage-"+ConfNameManagerAddr,
Key: "mavl-manage-" + ConfNameManagerAddr,
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{string(Nodes[0])}},
},
......@@ -101,31 +101,92 @@ func TestAccountManager(t *testing.T) {
stateDB.Set([]byte(item2.Key), types.Encode(item2))
item3 := &types.ConfigItem{
Key: "mavl-manage-"+ConfNameLockTime,
Key: "mavl-manage-" + ConfNameLockTime,
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{"15"}},
Arr: &types.ArrayConfig{Value: []string{"2"}},
},
}
stateDB.Set([]byte(item3.Key), types.Encode(item3))
//注册
tx1,err := CreateRegister(&et.Register{AccountID:"harrylee2015"},PrivKeyB)
if err !=nil {
tx1, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyB)
if err != nil {
t.Error(err)
}
Exec_Block(t,stateDB,kvdb,env,tx1)
tx2,err := CreateRegister(&et.Register{AccountID:"harrylee2015"},PrivKeyC)
err=Exec_Block(t,stateDB,kvdb,env,tx2)
assert.Equal(t,err,et.ErrAccountIDExist)
tx3,err := CreateRegister(&et.Register{AccountID:"harrylee2020"},PrivKeyC)
Exec_Block(t,stateDB,kvdb,env,tx3)
Exec_Block(t, stateDB, kvdb, env, tx1)
tx2, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyC)
err = Exec_Block(t, stateDB, kvdb, env, tx2)
assert.Equal(t, err, et.ErrAccountIDExist)
tx3, err := CreateRegister(&et.Register{AccountID: "harrylee2020"}, PrivKeyC)
Exec_Block(t, stateDB, kvdb, env, tx3)
//转账
tx4, err := CreateTransfer(&et.Transfer{FromAccountID: "harrylee2015", ToAccountID: "harrylee2020", Amount: 1e8, Asset: &et.Asset{Execer: "coins", Symbol: "bty"}}, PrivKeyB)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx4)
assert.Equal(t, err, nil)
//重置公钥
tx5, err := CreateReset(&et.ResetKey{Addr: "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs", AccountID: "harrylee2015"}, PrivKeyA)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx5)
//在锁定期内撤回请求
tx6, err := CreateApply(&et.Apply{Op: et.RevokeReset, AccountID: "harrylee2015"}, PrivKeyB)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx6)
assert.Equal(t, err, nil)
//重置公钥
tx5, err = CreateReset(&et.ResetKey{Addr: "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs", AccountID: "harrylee2015"}, PrivKeyA)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx5)
time.Sleep(time.Second)
//过了锁定期,申请生效
tx6, err = CreateApply(&et.Apply{Op: et.EnforceReset, AccountID: "harrylee2015"}, PrivKeyD)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx6)
tx7, _ := CreateTransfer(&et.Transfer{FromAccountID: "harrylee2015", ToAccountID: "harrylee2015", Amount: 1e8, Asset: &et.Asset{Execer: "coins", Symbol: "bty"}}, PrivKeyD)
err = Exec_Block(t, stateDB, kvdb, env, tx7)
assert.Equal(t, err, nil)
balance, err := Exec_QueryBalanceByID(&et.QueryBalanceByID{AccountID: "harrylee2015", Asset: &et.Asset{Symbol: "bty", Execer: "coins"}}, stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, balance.Balance, 199*types.Coin)
//将某个账户冻结
tx8, _ := CreateSupervise(&et.Supervise{
AccountIDs: []string{"harrylee2015"},
Op: et.Freeze,
}, PrivKeyA)
err = Exec_Block(t, stateDB, kvdb, env, tx8)
assert.Equal(t, err, nil)
//根据状态查询
accounts, err := Exec_QueryAccountsByStatus(et.Frozen, stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, accounts.Accounts[0].Status, et.Frozen)
balance, err = Exec_QueryBalanceByID(&et.QueryBalanceByID{Asset: &et.Asset{Execer: "coins", Symbol: "bty"}, AccountID: "harrylee2015"}, stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, balance.Frozen, 199*types.Coin)
tx4,err:=CreateTransfer(&et.Transfer{FromAccountID:"harrylee2015",ToAccountID:"harrylee2020",Amount:1e8,Asset:&et.Asset{Execer:"coins",Symbol:"bty"}},PrivKeyB)
if err !=nil {
//解冻账户
tx9, _ := CreateSupervise(&et.Supervise{
AccountIDs: []string{"harrylee2015"},
Op: et.UnFreeze,
}, PrivKeyA)
err = Exec_Block(t, stateDB, kvdb, env, tx9)
assert.Equal(t, err, nil)
//根据状态查询
accounts, err = Exec_QueryAccountsByStatus(et.Frozen, stateDB, kvdb)
assert.NotEqual(t, err, nil)
balance, err = Exec_QueryBalanceByID(&et.QueryBalanceByID{Asset: &et.Asset{Execer: "coins", Symbol: "bty"}, AccountID: "harrylee2015"}, stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, balance.Balance, 199*types.Coin)
//过期账户查询
time.Sleep(10 * time.Second)
t.Log(time.Now().Unix())
accs, err := Exec_QueryExpiredAccounts(time.Now().Unix(), stateDB, kvdb)
if err != nil {
t.Error(err)
}
err=Exec_Block(t,stateDB,kvdb,env,tx4)
assert.Equal(t,err,nil)
t.Log(accs)
}
func CreateRegister(register *et.Register, privKey string) (tx *types.Transaction, err error) {
......@@ -206,7 +267,7 @@ func CreateSupervise(supervise *et.Supervise, privKey string) (tx *types.Transac
func CreateApply(apply *et.Apply, privKey string) (tx *types.Transaction, err error) {
ety := types.LoadExecutorType(et.AccountmanagerX)
tx, err = ety.Create(et.NameSuperviseAction, apply)
tx, err = ety.Create(et.NameApplyAction, apply)
if err != nil {
return nil, err
}
......@@ -222,6 +283,7 @@ func CreateApply(apply *et.Apply, privKey string) (tx *types.Transaction, err er
}
return tx, nil
}
//模拟区块中交易得执行过程
func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ...*types.Transaction) error {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
......@@ -285,6 +347,22 @@ func Exec_QueryAccountByID(accountID string, stateDB db.KV, kvdb db.KVDB) (*et.A
return msg.(*et.Account), err
}
func Exec_QueryAccountByAddr(addr string, stateDB db.KV, kvdb db.KVDB) (*et.Account, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newAccountmanager()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
msg, err := exec.Query(et.FuncNameQueryAccountByAddr, types.Encode(&et.QueryAccountByAddr{Addr: addr}))
if err != nil {
return nil, err
}
return msg.(*et.Account), err
}
func Exec_QueryAccountsByStatus(status int32, stateDB db.KV, kvdb db.KVDB) (*et.ReplyAccountList, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
......@@ -296,14 +374,14 @@ func Exec_QueryAccountsByStatus(status int32, stateDB db.KV, kvdb db.KVDB) (*et.
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
msg, err := exec.Query(et.FuncNameQueryAccountsByStatus, types.Encode(&et.QueryAccountsByStatus{Status:status}))
msg, err := exec.Query(et.FuncNameQueryAccountsByStatus, types.Encode(&et.QueryAccountsByStatus{Status: status}))
if err != nil {
return nil, err
}
return msg.(*et.ReplyAccountList), err
}
func Exec_QueryExpiredAccounts(status int32, stateDB db.KV, kvdb db.KVDB) (*et.ReplyAccountList, error) {
func Exec_QueryBalanceByID(in *et.QueryBalanceByID, stateDB db.KV, kvdb db.KVDB) (*et.Balance, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newAccountmanager()
......@@ -313,14 +391,29 @@ func Exec_QueryExpiredAccounts(status int32, stateDB db.KV, kvdb db.KVDB) (*et.R
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
msg, err := exec.Query(et.FuncNameQueryExpiredAccounts, types.Encode(&et.QueryExpiredAccounts{}))
msg, err := exec.Query(et.FuncNameQueryBalanceByID, types.Encode(in))
if err != nil {
return nil, err
}
return msg.(*et.ReplyAccountList), err
return msg.(*et.Balance), err
}
func Exec_QueryExpiredAccounts(expiredtime int64, stateDB db.KV, kvdb db.KVDB) (*et.ReplyAccountList, error) {
cfg := types.NewChain33Config(types.GetDefaultCfgstring())
cfg.SetTitleOnlyForTest("chain33")
exec := newAccountmanager()
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
msg, err := exec.Query(et.FuncNameQueryExpiredAccounts, types.Encode(&et.QueryExpiredAccounts{ExpiredTime: expiredtime, Direction: 0}))
if err != nil {
return nil, err
}
return msg.(*et.ReplyAccountList), err
}
func signTx(tx *types.Transaction, hexPrivKey string) (*types.Transaction, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName("", signType))
......
......@@ -24,7 +24,7 @@ var (
DefaultActiveTime = int64(5 * 360 * 24 * 3600)
//默认密钥重置锁定期
DefaultLockTime = int64(15 * 24 * 3600)
//默认管理员地址
DefaultManagerAddr = "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
)
......@@ -48,9 +48,9 @@ func NewAction(e *accountmanager, tx *types.Transaction, index int) *Action {
e.GetBlockTime(), e.GetHeight(), dapp.ExecAddress(string(tx.Execer)), e.GetLocalDB(), index, e.GetAPI()}
}
//GetIndex get index
//GetIndex get index 主键索引
func (a *Action) GetIndex() int64 {
return a.blocktime*int64(types.MaxTxsPerBlock) + int64(a.index)
return a.height*types.MaxTxsPerBlock + int64(a.index)
}
//GetKVSet get kv set
......@@ -117,6 +117,7 @@ func (a *Action) Reset(payload *et.ResetKey) (*types.Receipt, error) {
func (a *Action) Transfer(payload *et.Transfer) (*types.Receipt, error) {
var logs []*types.ReceiptLog
var kvs []*types.KeyValue
cfg := a.api.GetConfig()
account1, err := findAccountByID(a.localDB, payload.FromAccountID)
if err != nil {
elog.Error("Transfer", "fromAccountID", payload.FromAccountID, "err", et.ErrAccountIDNotExist)
......@@ -126,6 +127,33 @@ func (a *Action) Transfer(payload *et.Transfer) (*types.Receipt, error) {
elog.Error("Transfer", "fromaddr", a.fromaddr, "err", et.ErrAccountIDNotPermiss)
return nil, et.ErrAccountIDNotPermiss
}
//如果prevAddr地址不为空,先查看余额,将该地址下面得资产划转到新得公钥地址下
if account1.PrevAddr != "" {
assetDB, err := account.NewAccountDB(cfg, payload.Asset.GetExecer(), payload.Asset.GetSymbol(), a.statedb)
if err != nil {
return nil, err
}
prevAccount := assetDB.LoadExecAccount(account1.PrevAddr, a.execaddr)
if prevAccount.Balance > 0 {
receipt, err := assetDB.ExecTransfer(account1.PrevAddr, account1.Addr, a.execaddr, prevAccount.Balance)
if err != nil {
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
}
}
if payload.FromAccountID == payload.ToAccountID {
re := &et.TransferReceipt{
FromAccount: account1,
ToAccount: account1,
Index: a.GetIndex(),
}
receiptlog := &types.ReceiptLog{Ty: et.TyTransferLog, Log: types.Encode(re)}
logs = append(logs, receiptlog)
receipts := &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}
return receipts, nil
}
account2, err := findAccountByID(a.localDB, payload.ToAccountID)
if err != nil {
elog.Error("Transfer,check to accountID", "toAccountID", payload.ToAccountID, "err", et.ErrAccountIDNotExist)
......@@ -135,7 +163,7 @@ func (a *Action) Transfer(payload *et.Transfer) (*types.Receipt, error) {
elog.Error("Transfer", "ToAccountID", account2.AccountID, "err", et.ErrAccountIDNotPermiss)
return nil, et.ErrAccountIDNotPermiss
}
cfg := a.api.GetConfig()
assetDB, err := account.NewAccountDB(cfg, payload.Asset.GetExecer(), payload.Asset.GetSymbol(), a.statedb)
if err != nil {
return nil, err
......@@ -155,7 +183,7 @@ func (a *Action) Transfer(payload *et.Transfer) (*types.Receipt, error) {
re := &et.TransferReceipt{
FromAccount: account1,
ToAccount: account2,
BlockTime: a.blocktime,
Index: a.GetIndex(),
}
receiptlog := &types.ReceiptLog{Ty: et.TyTransferLog, Log: types.Encode(re)}
logs = append(logs, receiptlog)
......@@ -170,31 +198,50 @@ func (a *Action) Supervise(payload *et.Supervise) (*types.Receipt, error) {
if managerAddr != a.fromaddr {
return nil, et.ErrNotAdmin
}
coinsAssetDB, err := account.NewAccountDB(cfg, "coins", cfg.GetCoinSymbol(), a.statedb)
if err != nil {
return nil, err
}
var logs []*types.ReceiptLog
var kvs []*types.KeyValue
var re et.SuperviseReceipt
for _, ID := range payload.AccountIDs {
account, err := findAccountByID(a.localDB, ID)
accountM, err := findAccountByID(a.localDB, ID)
if err != nil {
elog.Error("Supervise", "AccountID", ID, "err", et.ErrAccountIDNotExist)
return nil, et.ErrAccountIDNotExist
}
switch re.Op {
switch payload.Op {
case et.Freeze:
account.Status = et.Frozen
//TODO 账户冻结,还需要冻结账户地址相应得资产,这里因为查不到所有token资产,所以只冻结主币
accountM.Status = et.Frozen
coinsAccount := coinsAssetDB.LoadExecAccount(accountM.Addr, a.execaddr)
receipt, err := coinsAssetDB.ExecFrozen(accountM.Addr, a.execaddr, coinsAccount.Balance)
if err != nil {
elog.Error("Supervise ExecFrozen", "AccountID", ID, "err", err)
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
case et.UnFreeze:
account.Status = et.Normal
accountM.Status = et.Normal
coinsAccount := coinsAssetDB.LoadExecAccount(accountM.Addr, a.execaddr)
receipt, err := coinsAssetDB.ExecActive(accountM.Addr, a.execaddr, coinsAccount.Frozen)
if err != nil {
elog.Error("Supervise ExecActive", "AccountID", ID, "err", err)
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
case et.AddExpire:
cfg := a.api.GetConfig()
defaultActiveTime := getConfValue(cfg, a.statedb, ConfNameActiveTime, DefaultActiveTime)
account.Status = et.Normal
account.ExpireTime = a.blocktime + defaultActiveTime
accountM.Status = et.Normal
accountM.ExpireTime = a.blocktime + defaultActiveTime
}
re.Accounts = append(re.Accounts, account)
re.Accounts = append(re.Accounts, accountM)
}
re.Op = payload.Op
re.BlockTime = a.blocktime
re.Index = a.GetIndex()
receiptlog := &types.ReceiptLog{Ty: et.TySuperviseLog, Log: types.Encode(&re)}
logs = append(logs, receiptlog)
receipts := &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}
......@@ -203,37 +250,52 @@ func (a *Action) Supervise(payload *et.Supervise) (*types.Receipt, error) {
func (a *Action) Apply(payload *et.Apply) (*types.Receipt, error) {
var logs []*types.ReceiptLog
account, err := findAccountByID(a.localDB, payload.AccountID)
var kvs []*types.KeyValue
accountM, err := findAccountByID(a.localDB, payload.AccountID)
if err != nil {
elog.Error("Apply", "AccountID", payload.AccountID, "err", et.ErrAccountIDNotExist)
return nil, et.ErrAccountIDNotExist
}
switch payload.Op {
case et.RevokeReset:
if account.Status != et.Locked || account.PrevAddr != a.fromaddr {
if accountM.Status != et.Locked || accountM.PrevAddr != a.fromaddr {
elog.Error("Apply", "fromaddr", a.fromaddr, "err", et.ErrAccountIDNotPermiss)
return nil, et.ErrAccountIDNotPermiss
}
account.LockTime = 0
account.Status = et.Normal
account.Addr = a.fromaddr
accountM.LockTime = 0
accountM.Status = et.Normal
accountM.Addr = a.fromaddr
case et.EnforceReset:
if account.Status != et.Locked || account.Addr != a.fromaddr {
if accountM.Status != et.Locked || accountM.Addr != a.fromaddr {
elog.Error("Apply", "fromaddr", a.fromaddr, "err", et.ErrAccountIDNotPermiss)
return nil, et.ErrAccountIDNotPermiss
}
account.LockTime = 0
account.Status = et.Normal
//TODO 资产转移,放在转transfer中执行
accountM.LockTime = 0
accountM.Status = et.Normal
//TODO 这里只做coins主笔资产得自动划转,token资产转移,放在转transfer中执行 fromAccountID == toAccountID
cfg := a.api.GetConfig()
coinsAssetDB, err := account.NewAccountDB(cfg, "coins", cfg.GetCoinSymbol(), a.statedb)
if err != nil {
return nil, err
}
coinsAccount := coinsAssetDB.LoadExecAccount(accountM.PrevAddr, a.execaddr)
receipt, err := coinsAssetDB.ExecTransfer(accountM.PrevAddr, accountM.Addr, a.execaddr, coinsAccount.Balance)
if err != nil {
elog.Error("Apply ExecTransfer", "AccountID", accountM.AccountID, "err", err)
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
}
re := &et.AccountReceipt{
Account: account,
Account: accountM,
}
receiptlog := &types.ReceiptLog{Ty: et.TyApplyLog, Log: types.Encode(re)}
logs = append(logs, receiptlog)
receipts := &types.Receipt{Ty: types.ExecOk, KV: nil, Logs: logs}
receipts := &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}
return receipts, nil
}
......@@ -308,24 +370,26 @@ func getConfigKey(key string, db dbm.KV) ([]byte, error) {
return value, nil
}
func findAccountListByIndex(localdb dbm.KV, direction int32, primaryKey string) (*et.ReplyAccountList, error) {
//正序遍历数据,与传入时间进行对比,看是否逾期
func findAccountListByIndex(localdb dbm.KV, expireTime int64, primaryKey string) (*et.ReplyAccountList, error) {
table := NewAccountTable(localdb)
first := []byte(fmt.Sprintf("%016d", time.Now().Unix()*int64(types.MaxTxsPerBlock)))
var rows []*tab.Row
var err error
if primaryKey == "" { //第一次查询,默认展示最新得成交记录
rows, err = table.ListIndex("index", nil, first, et.Count, direction)
rows, err = table.ListIndex("index", nil, nil, et.Count, et.ListASC)
} else {
rows, err = table.ListIndex("index", nil, []byte(primaryKey), et.Count, direction)
rows, err = table.ListIndex("index", nil, []byte(primaryKey), et.Count, et.ListASC)
}
if err != nil {
elog.Error("findAccountListByIndex.", "index", first, "err", err.Error())
elog.Error("findAccountListByIndex.", "index", primaryKey, "err", err.Error())
return nil, err
}
var reply et.ReplyAccountList
for _, row := range rows {
account := row.Data.(*et.Account)
if account.ExpireTime > expireTime {
break
}
//状态变成逾期状态
account.Status = et.Expired
reply.Accounts = append(reply.Accounts, account)
......@@ -353,9 +417,25 @@ func findAccountByID(localdb dbm.KV, accountID string) (*et.Account, error) {
return nil, types.ErrNotFound
}
func findAccountByAddr(localdb dbm.KV, addr string) (*et.Account, error) {
table := NewAccountTable(localdb)
prefix := []byte(fmt.Sprintf("%s", addr))
//第一次查询,默认展示最新得成交记录
rows, err := table.ListIndex("addr", prefix, nil, 1, et.ListDESC)
if err != nil {
elog.Error("findAccountByAddr.", "prefix", prefix, "err", err.Error())
return nil, err
}
for _, row := range rows {
account := row.Data.(*et.Account)
return account, nil
}
return nil, types.ErrNotFound
}
func findAccountListByStatus(localdb dbm.KV, status, direction int32, primaryKey string) (*et.ReplyAccountList, error) {
if status == et.Expired {
return findAccountListByIndex(localdb, direction, primaryKey)
return findAccountListByIndex(localdb, time.Now().Unix(), primaryKey)
}
table := NewAccountTable(localdb)
prefix := []byte(fmt.Sprintf("%d", status))
......@@ -368,7 +448,7 @@ func findAccountListByStatus(localdb dbm.KV, status, direction int32, primaryKey
rows, err = table.ListIndex("status", prefix, []byte(primaryKey), et.Count, direction)
}
if err != nil {
elog.Error("findAccountListByStatus.", "status", prefix, "err", err.Error())
elog.Error("findAccountListByStatus.", "status", status, "err", err.Error())
return nil, err
}
var reply et.ReplyAccountList
......@@ -382,3 +462,24 @@ func findAccountListByStatus(localdb dbm.KV, status, direction int32, primaryKey
}
return &reply, nil
}
func queryBalanceByID(statedb, localdb dbm.KV, cfg *types.Chain33Config, execName string, in *et.QueryBalanceByID) (*et.Balance, error) {
acc, err := findAccountByID(localdb, in.AccountID)
if err != nil {
return nil, err
}
assetDB, err := account.NewAccountDB(cfg, in.Asset.GetExecer(), in.Asset.GetSymbol(), statedb)
if err != nil {
return nil, err
}
var balance et.Balance
if acc.PrevAddr != "" {
prevAccount := assetDB.LoadExecAccount(acc.PrevAddr, dapp.ExecAddress(execName))
balance.Balance += prevAccount.Balance
balance.Frozen += prevAccount.Frozen
}
currAccount := assetDB.LoadExecAccount(acc.Addr, dapp.ExecAddress(execName))
balance.Balance += currAccount.Balance
balance.Frozen += currAccount.Frozen
return &balance, nil
}
......@@ -15,7 +15,7 @@ func (a *accountmanager) Exec_Register(payload *aty.Register, tx *types.Transact
return action.Register(payload)
}
func (a *accountmanager) Exec_Resetkey(payload *aty.ResetKey, tx *types.Transaction, index int) (*types.Receipt, error) {
func (a *accountmanager) Exec_ResetKey(payload *aty.ResetKey, tx *types.Transaction, index int) (*types.Receipt, error) {
action := NewAction(a, tx, index)
return action.Reset(payload)
}
......@@ -31,5 +31,6 @@ func (a *accountmanager) Exec_Supervise(payload *aty.Supervise, tx *types.Transa
}
func (a *accountmanager) Exec_Apply(payload *aty.Apply, tx *types.Transaction, index int) (*types.Receipt, error) {
return nil, types.ErrActionNotSupport
action := NewAction(a, tx, index)
return action.Apply(payload)
}
......@@ -94,7 +94,20 @@ func (a *accountmanager) ExecLocal_Transfer(payload *et.Transfer, tx *types.Tran
for _, log := range receiptData.Logs {
switch log.Ty {
case et.TyResetLog:
//账户信息不变更,不需要处理
receipt := &et.TransferReceipt{}
if err := types.Decode(log.Log, receipt); err != nil {
return nil, err
}
accountTable := NewAccountTable(a.GetLocalDB())
err := accountTable.Replace(receipt.FromAccount)
if err != nil {
return nil, err
}
kvs, err := accountTable.Save()
if err != nil {
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
}
}
}
......@@ -112,17 +125,39 @@ func (a *accountmanager) ExecLocal_Supervise(payload *et.Supervise, tx *types.Tr
return nil, err
}
accountTable := NewAccountTable(a.GetLocalDB())
for _, account := range receipt.Accounts {
err := accountTable.Replace(account)
//当时续期操作得话,需要重建
if receipt.Op == et.AddExpire {
for _, account := range receipt.Accounts {
err := accountTable.DelRow(account)
if err != nil {
return nil, err
}
//重置主键
account.Index = receipt.Index
err = accountTable.Replace(account)
if err != nil {
return nil, err
}
}
kvs, err := accountTable.Save()
if err != nil {
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
} else {
for _, account := range receipt.Accounts {
err := accountTable.Replace(account)
if err != nil {
return nil, err
}
}
kvs, err := accountTable.Save()
if err != nil {
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
}
kvs, err := accountTable.Save()
if err != nil {
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
}
}
}
......
......@@ -10,6 +10,11 @@ func (s *accountmanager) Query_QueryAccountByID(in *et.QueryAccountByID) (types.
return findAccountByID(s.GetLocalDB(), in.AccountID)
}
//根据ID查询账户信息
func (s *accountmanager) Query_QueryAccountByAddr(in *et.QueryAccountByAddr) (types.Message, error) {
return findAccountByAddr(s.GetLocalDB(), in.Addr)
}
//根据状态查询账户列表|| 账户状态 1 正常, 2表示冻结, 3表示锁定 4,过期注销
func (s *accountmanager) Query_QueryAccountsByStatus(in *et.QueryAccountsByStatus) (types.Message, error) {
return findAccountListByStatus(s.GetLocalDB(), in.Status, in.Direction, in.PrimaryKey)
......@@ -17,5 +22,10 @@ func (s *accountmanager) Query_QueryAccountsByStatus(in *et.QueryAccountsByStatu
//查询逾期注销的账户列表
func (s *accountmanager) Query_QueryExpiredAccounts(in *et.QueryExpiredAccounts) (types.Message, error) {
return findAccountListByIndex(s.GetLocalDB(), in.Direction, in.PrimaryKey)
return findAccountListByIndex(s.GetLocalDB(), in.ExpiredTime, in.PrimaryKey)
}
//根据ID查询账户余额
func (s *accountmanager) Query_QueryBalanceByID(in *et.QueryBalanceByID) (types.Message, error) {
return queryBalanceByID(s.GetStateDB(), s.GetLocalDB(), s.GetAPI().GetConfig(), s.GetName(), in)
}
......@@ -25,7 +25,7 @@ var opt_account = &table.Option{
Prefix: KeyPrefixLocalDB,
Name: "account",
Primary: "index",
Index: []string{"status", "accountID"},
Index: []string{"status", "accountID", "addr"},
}
//状态数据库中存储具体账户信息
......@@ -74,7 +74,9 @@ func (m *AccountRow) Get(key string) ([]byte, error) {
} else if key == "status" {
return []byte(fmt.Sprintf("%d", m.Status)), nil
} else if key == "index" {
return []byte(fmt.Sprintf("%014d", m.GetIndex())), nil
return []byte(fmt.Sprintf("%018d", m.GetIndex())), nil
} else if key == "addr" {
return []byte(fmt.Sprintf("%s", m.GetAddr())), nil
}
return nil, types.ErrNotFound
}
package executor
import (
"fmt"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"testing"
"time"
)
func Test(t *testing.T) {
t.Log(fmt.Sprintf("%-s", "aaaa100000b"))
}
func TestAccountTable(t *testing.T) {
_, _, kvdb := util.CreateTestDB()
table:=NewAccountTable(kvdb)
now :=time.Now().Unix()
row1:= &et.Account{Index:now*int64(types.MaxTxsPerBlock),AccountID:"harry2015",Status:1,ExpireTime:now+10,Addr:"xxxx"}
row2:= &et.Account{Index:now*int64(types.MaxTxsPerBlock)+1,AccountID:"harry2020",Status:1,ExpireTime:now,Addr:"xxxx"}
table.Add(row1)
table.Add(row2)
kvs,err:=table.Save()
if err !=nil {
t.Error(err)
}
for _,kv :=range kvs{
kvdb.Set(kv.Key,kv.Value)
}
time.Sleep(2*time.Second)
list,err:=findAccountListByIndex(kvdb,time.Now().Unix(),"")
if err !=nil {
t.Error(err)
}
t.Log(list)
list,err=findAccountListByStatus(kvdb,et.Normal,0,"")
if err !=nil {
t.Error(err)
}
t.Log(list)
row1.Status=et.Frozen
err=table.Replace(row1)
if err !=nil {
t.Error(err)
}
kvs,err=table.Save()
if err !=nil {
t.Error(err)
}
for _,kv :=range kvs{
kvdb.Set(kv.Key,kv.Value)
}
list,err=findAccountListByStatus(kvdb,et.Frozen,0,"")
if err !=nil {
t.Error(err)
}
t.Log(list)
}
\ No newline at end of file
......@@ -100,21 +100,24 @@ message TransferReceipt{
account ToAccount = 2;
int64 blockTime = 3;
int64 index = 3;
}
//回执日志
message SuperviseReceipt{
repeated account accounts = 1;
int32 op = 2;
int64 blockTime = 3;
int64 index = 3;
}
message QueryExpiredAccounts{
string primaryKey = 1;
//第一次需要传入逾期时间,时间戳
int64 expiredTime = 2;
//单页返回多少条记录,默认返回10条
// 0降序,1升序,默认降序
int32 direction = 2;
int32 direction = 3;
}
message QueryAccountsByStatus{
......@@ -129,6 +132,21 @@ message QueryAccountsByStatus{
message QueryAccountByID {
string accountID = 1;
}
message QueryAccountByAddr {
string addr = 1;
}
message QueryBalanceByID {
string accountID = 1;
asset asset = 2;
}
message balance {
int64 balance = 1;
int64 frozen = 2;
}
service accountmanager {
}
......@@ -30,6 +30,8 @@ const (
FuncNameQueryAccountByID = "QueryAccountByID"
FuncNameQueryAccountsByStatus = "QueryAccountsByStatus"
FuncNameQueryExpiredAccounts = "QueryExpiredAccounts"
FuncNameQueryAccountByAddr = "QueryAccountByAddr"
FuncNameQueryBalanceByID = "QueryBalanceByID"
)
// log类型id值
......
......@@ -24,6 +24,9 @@ It has these top-level messages:
QueryExpiredAccounts
QueryAccountsByStatus
QueryAccountByID
QueryAccountByAddr
QueryBalanceByID
Balance
*/
package types
......@@ -565,7 +568,7 @@ func (m *ReplyAccountList) GetPrimaryKey() string {
type TransferReceipt struct {
FromAccount *Account `protobuf:"bytes,1,opt,name=FromAccount" json:"FromAccount,omitempty"`
ToAccount *Account `protobuf:"bytes,2,opt,name=ToAccount" json:"ToAccount,omitempty"`
BlockTime int64 `protobuf:"varint,3,opt,name=blockTime" json:"blockTime,omitempty"`
Index int64 `protobuf:"varint,3,opt,name=index" json:"index,omitempty"`
}
func (m *TransferReceipt) Reset() { *m = TransferReceipt{} }
......@@ -587,18 +590,18 @@ func (m *TransferReceipt) GetToAccount() *Account {
return nil
}
func (m *TransferReceipt) GetBlockTime() int64 {
func (m *TransferReceipt) GetIndex() int64 {
if m != nil {
return m.BlockTime
return m.Index
}
return 0
}
// 回执日志
type SuperviseReceipt struct {
Accounts []*Account `protobuf:"bytes,1,rep,name=accounts" json:"accounts,omitempty"`
Op int32 `protobuf:"varint,2,opt,name=op" json:"op,omitempty"`
BlockTime int64 `protobuf:"varint,3,opt,name=blockTime" json:"blockTime,omitempty"`
Accounts []*Account `protobuf:"bytes,1,rep,name=accounts" json:"accounts,omitempty"`
Op int32 `protobuf:"varint,2,opt,name=op" json:"op,omitempty"`
Index int64 `protobuf:"varint,3,opt,name=index" json:"index,omitempty"`
}
func (m *SuperviseReceipt) Reset() { *m = SuperviseReceipt{} }
......@@ -620,18 +623,20 @@ func (m *SuperviseReceipt) GetOp() int32 {
return 0
}
func (m *SuperviseReceipt) GetBlockTime() int64 {
func (m *SuperviseReceipt) GetIndex() int64 {
if m != nil {
return m.BlockTime
return m.Index
}
return 0
}
type QueryExpiredAccounts struct {
PrimaryKey string `protobuf:"bytes,1,opt,name=primaryKey" json:"primaryKey,omitempty"`
// 第一次需要传入逾期时间,时间戳
ExpiredTime int64 `protobuf:"varint,2,opt,name=expiredTime" json:"expiredTime,omitempty"`
// 单页返回多少条记录,默认返回10条
// 0降序,1升序,默认降序
Direction int32 `protobuf:"varint,2,opt,name=direction" json:"direction,omitempty"`
Direction int32 `protobuf:"varint,3,opt,name=direction" json:"direction,omitempty"`
}
func (m *QueryExpiredAccounts) Reset() { *m = QueryExpiredAccounts{} }
......@@ -646,6 +651,13 @@ func (m *QueryExpiredAccounts) GetPrimaryKey() string {
return ""
}
func (m *QueryExpiredAccounts) GetExpiredTime() int64 {
if m != nil {
return m.ExpiredTime
}
return 0
}
func (m *QueryExpiredAccounts) GetDirection() int32 {
if m != nil {
return m.Direction
......@@ -704,6 +716,70 @@ func (m *QueryAccountByID) GetAccountID() string {
return ""
}
type QueryAccountByAddr struct {
Addr string `protobuf:"bytes,1,opt,name=addr" json:"addr,omitempty"`
}
func (m *QueryAccountByAddr) Reset() { *m = QueryAccountByAddr{} }
func (m *QueryAccountByAddr) String() string { return proto.CompactTextString(m) }
func (*QueryAccountByAddr) ProtoMessage() {}
func (*QueryAccountByAddr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *QueryAccountByAddr) GetAddr() string {
if m != nil {
return m.Addr
}
return ""
}
type QueryBalanceByID struct {
AccountID string `protobuf:"bytes,1,opt,name=accountID" json:"accountID,omitempty"`
Asset *Asset `protobuf:"bytes,2,opt,name=asset" json:"asset,omitempty"`
}
func (m *QueryBalanceByID) Reset() { *m = QueryBalanceByID{} }
func (m *QueryBalanceByID) String() string { return proto.CompactTextString(m) }
func (*QueryBalanceByID) ProtoMessage() {}
func (*QueryBalanceByID) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
func (m *QueryBalanceByID) GetAccountID() string {
if m != nil {
return m.AccountID
}
return ""
}
func (m *QueryBalanceByID) GetAsset() *Asset {
if m != nil {
return m.Asset
}
return nil
}
type Balance struct {
Balance int64 `protobuf:"varint,1,opt,name=balance" json:"balance,omitempty"`
Frozen int64 `protobuf:"varint,2,opt,name=frozen" json:"frozen,omitempty"`
}
func (m *Balance) Reset() { *m = Balance{} }
func (m *Balance) String() string { return proto.CompactTextString(m) }
func (*Balance) ProtoMessage() {}
func (*Balance) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (m *Balance) GetBalance() int64 {
if m != nil {
return m.Balance
}
return 0
}
func (m *Balance) GetFrozen() int64 {
if m != nil {
return m.Frozen
}
return 0
}
func init() {
proto.RegisterType((*Accountmanager)(nil), "types.Accountmanager")
proto.RegisterType((*AccountmanagerAction)(nil), "types.AccountmanagerAction")
......@@ -721,6 +797,9 @@ func init() {
proto.RegisterType((*QueryExpiredAccounts)(nil), "types.QueryExpiredAccounts")
proto.RegisterType((*QueryAccountsByStatus)(nil), "types.QueryAccountsByStatus")
proto.RegisterType((*QueryAccountByID)(nil), "types.QueryAccountByID")
proto.RegisterType((*QueryAccountByAddr)(nil), "types.QueryAccountByAddr")
proto.RegisterType((*QueryBalanceByID)(nil), "types.QueryBalanceByID")
proto.RegisterType((*Balance)(nil), "types.balance")
}
// Reference imports to suppress errors if they are not otherwise used.
......@@ -764,46 +843,50 @@ var _Accountmanager_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("accountmanager.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 647 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xad, 0xe3, 0x3a, 0x8d, 0xa7, 0x90, 0x46, 0xab, 0x80, 0x2c, 0x84, 0x50, 0xb4, 0xea, 0x21,
0x42, 0x50, 0x55, 0x45, 0x08, 0x09, 0xb8, 0xb8, 0x2a, 0xa8, 0x15, 0x5c, 0xd8, 0xe6, 0x8c, 0xe4,
0x3a, 0xd3, 0xca, 0x22, 0x8e, 0x57, 0xbb, 0x9b, 0xaa, 0xfe, 0x13, 0x70, 0xe1, 0x0f, 0xf2, 0x4f,
0xd0, 0x7e, 0xd9, 0x4e, 0x0a, 0xad, 0x7a, 0xf3, 0xbc, 0xf7, 0x3c, 0x33, 0xfb, 0x66, 0xd6, 0x86,
0x71, 0x96, 0xe7, 0xd5, 0x6a, 0xa9, 0xca, 0x6c, 0x99, 0x5d, 0xa1, 0x38, 0xe0, 0xa2, 0x52, 0x15,
0x89, 0x54, 0xcd, 0x51, 0xd2, 0x11, 0x0c, 0xd3, 0x35, 0x9a, 0xfe, 0xee, 0xc1, 0x78, 0x1d, 0x4a,
0x73, 0x55, 0x54, 0x4b, 0xf2, 0x1a, 0x06, 0x02, 0xaf, 0x0a, 0xa9, 0x50, 0x24, 0xc1, 0x24, 0x98,
0xee, 0x1e, 0xed, 0x1d, 0x98, 0x24, 0x07, 0xcc, 0xc1, 0xa7, 0x5b, 0xac, 0x91, 0x58, 0xb9, 0x44,
0xf5, 0x05, 0xeb, 0xa4, 0xb7, 0x21, 0xb7, 0xb0, 0x95, 0xdb, 0x67, 0x2d, 0x57, 0x22, 0x5b, 0xca,
0x4b, 0x14, 0x49, 0xb8, 0x26, 0x9f, 0x39, 0x58, 0xcb, 0xbd, 0x84, 0x1c, 0x42, 0x2c, 0x57, 0x1c,
0xc5, 0x75, 0x21, 0x31, 0xd9, 0x36, 0xfa, 0x91, 0xd3, 0x9f, 0x7b, 0xfc, 0x74, 0x8b, 0xb5, 0x22,
0xb2, 0x0f, 0x51, 0xc6, 0xf9, 0xa2, 0x4e, 0x22, 0xa3, 0x7e, 0xe4, 0xd4, 0xa9, 0xc6, 0x4e, 0xb7,
0x98, 0x25, 0xc9, 0x10, 0x7a, 0xaa, 0x4e, 0xfa, 0x93, 0x60, 0x1a, 0xb1, 0x9e, 0xaa, 0x8f, 0x77,
0x20, 0xba, 0xce, 0x16, 0x2b, 0xa4, 0x53, 0x18, 0xf8, 0x63, 0x92, 0xe7, 0x10, 0x3b, 0x4f, 0xcf,
0x4e, 0x8c, 0x15, 0x31, 0x6b, 0x01, 0xfa, 0x51, 0x2b, 0xdd, 0xa9, 0xee, 0x54, 0x12, 0x02, 0xdb,
0xd9, 0x7c, 0x2e, 0x8c, 0x3d, 0x31, 0x33, 0xcf, 0xf4, 0x2d, 0x44, 0xa6, 0xa5, 0x7b, 0x5e, 0x1d,
0x42, 0xaf, 0xe2, 0xe6, 0xc5, 0x88, 0xf5, 0x2a, 0x4e, 0xdf, 0x41, 0x94, 0x49, 0x89, 0x8a, 0x3c,
0x85, 0x3e, 0xde, 0x60, 0xee, 0x66, 0x14, 0x33, 0x17, 0x69, 0x5c, 0xd6, 0xe5, 0x45, 0xb5, 0x70,
0xd5, 0x5c, 0x44, 0x7f, 0x06, 0x30, 0xf0, 0x0e, 0x13, 0x0a, 0x51, 0xaa, 0xb3, 0xb8, 0xf9, 0x7a,
0x8f, 0x4c, 0x66, 0x66, 0x29, 0xb2, 0x0f, 0x8f, 0x2f, 0x45, 0x55, 0xa6, 0x4d, 0x6f, 0x36, 0xdf,
0x3a, 0x48, 0x26, 0xb0, 0xab, 0xaa, 0x56, 0x13, 0x1a, 0x4d, 0x17, 0xd2, 0x0d, 0x65, 0xa5, 0x7e,
0x36, 0xe3, 0x0b, 0x99, 0x8b, 0xe8, 0x07, 0x88, 0x9b, 0x09, 0x92, 0x17, 0x00, 0xcd, 0x99, 0x65,
0x12, 0x4c, 0xc2, 0x69, 0xcc, 0x3a, 0xc8, 0x2d, 0x1b, 0xfe, 0x04, 0xb0, 0xe3, 0xe8, 0x87, 0x7b,
0x4f, 0x9e, 0xc1, 0x80, 0x0b, 0xbc, 0x4e, 0x35, 0x6e, 0x3b, 0x6e, 0x62, 0xe3, 0x9f, 0xca, 0xd4,
0x4a, 0x9a, 0x76, 0x23, 0xe6, 0x22, 0xdd, 0x61, 0x2e, 0x30, 0x53, 0x38, 0x2b, 0x4a, 0x34, 0xbb,
0x15, 0xb2, 0x0e, 0xa2, 0x79, 0xbc, 0xe1, 0x85, 0xb0, 0x7c, 0xdf, 0xf2, 0x2d, 0xa2, 0x6b, 0x2e,
0xaa, 0xfc, 0x87, 0x61, 0x77, 0x0c, 0xdb, 0xc4, 0x64, 0x0c, 0x51, 0xb1, 0x9c, 0xe3, 0x4d, 0x32,
0x30, 0x84, 0x0d, 0xe8, 0xfb, 0xe6, 0xca, 0x32, 0xcc, 0xb1, 0xe0, 0x8a, 0x4c, 0x9b, 0x43, 0xbb,
0xc1, 0x0d, 0xfd, 0xe0, 0x9c, 0xce, 0xd3, 0xf4, 0x3b, 0x8c, 0x18, 0xf2, 0x45, 0xed, 0x12, 0x7c,
0x2d, 0xa4, 0x22, 0x2f, 0x61, 0xe0, 0x68, 0xeb, 0xf0, 0xed, 0xd7, 0x1b, 0x5e, 0x9f, 0x86, 0x8b,
0xa2, 0xcc, 0x44, 0xed, 0xaf, 0x75, 0xcc, 0x3a, 0x08, 0xfd, 0x15, 0xc0, 0x9e, 0xdf, 0x26, 0xdf,
0xdd, 0x21, 0xec, 0x7e, 0x6e, 0x77, 0xe3, 0x3f, 0x1d, 0x76, 0x25, 0xe4, 0x15, 0xc4, 0x33, 0xbf,
0x29, 0xee, 0xdb, 0xb1, 0xa9, 0x6f, 0x05, 0x7a, 0xce, 0x17, 0x8d, 0x85, 0xa1, 0x71, 0xaa, 0x05,
0xe8, 0x02, 0x46, 0xcd, 0x3a, 0xf9, 0x8e, 0x1e, 0x72, 0xe2, 0x8d, 0x0d, 0xbb, 0xa7, 0xda, 0x0c,
0xc6, 0xdf, 0x56, 0x28, 0xea, 0x4f, 0x66, 0xc0, 0xf3, 0xf4, 0xdf, 0xbe, 0x05, 0x9b, 0xbe, 0xe9,
0xac, 0xf3, 0x42, 0xa0, 0xf9, 0xd0, 0xba, 0x62, 0x2d, 0x40, 0x4b, 0x78, 0x62, 0xb2, 0xfa, 0x74,
0xc7, 0xf5, 0xb9, 0x5d, 0xbe, 0x76, 0x29, 0x83, 0xcd, 0xa5, 0xec, 0x94, 0x0b, 0xef, 0x2e, 0x17,
0x6d, 0x96, 0x3b, 0x84, 0x51, 0xb7, 0xdc, 0x71, 0x7d, 0x76, 0x72, 0xf7, 0x65, 0x3a, 0x1a, 0xc1,
0x70, 0xfd, 0x27, 0x73, 0xd1, 0x37, 0x7f, 0x99, 0x37, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x77,
0x0c, 0x4d, 0x85, 0x7d, 0x06, 0x00, 0x00,
// 719 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcf, 0x6a, 0x1b, 0x3f,
0x10, 0xce, 0x7a, 0xb3, 0xb6, 0x77, 0xf2, 0xfb, 0x39, 0x46, 0xb8, 0x61, 0x29, 0xa5, 0x18, 0x91,
0x83, 0x29, 0x6d, 0x08, 0x29, 0xa5, 0xd0, 0xf4, 0x62, 0x93, 0x96, 0x84, 0xf6, 0x52, 0xc5, 0xe7,
0x82, 0xb2, 0x56, 0xc2, 0x52, 0xdb, 0xbb, 0x68, 0x65, 0x93, 0xed, 0x0b, 0xf4, 0xd6, 0x53, 0x5f,
0xb0, 0x6f, 0x52, 0x34, 0x92, 0xf6, 0x5f, 0x9a, 0x86, 0x9c, 0xbc, 0xf3, 0x7d, 0x9f, 0x66, 0x46,
0x33, 0xa3, 0x31, 0x8c, 0x78, 0x1c, 0xa7, 0x9b, 0xb5, 0x5a, 0xf1, 0x35, 0xbf, 0x11, 0xf2, 0x28,
0x93, 0xa9, 0x4a, 0x49, 0xa0, 0x8a, 0x4c, 0xe4, 0x74, 0x08, 0x83, 0x69, 0x83, 0xa6, 0xbf, 0x3a,
0x30, 0x6a, 0x42, 0xd3, 0x58, 0x25, 0xe9, 0x9a, 0xbc, 0x82, 0xbe, 0x14, 0x37, 0x49, 0xae, 0x84,
0x8c, 0xbc, 0xb1, 0x37, 0xd9, 0x3b, 0xd9, 0x3f, 0x42, 0x27, 0x47, 0xcc, 0xc2, 0xe7, 0x3b, 0xac,
0x94, 0x18, 0x79, 0x2e, 0xd4, 0x27, 0x51, 0x44, 0x9d, 0x96, 0xdc, 0xc0, 0x46, 0x6e, 0xbe, 0xb5,
0x5c, 0x49, 0xbe, 0xce, 0xaf, 0x85, 0x8c, 0xfc, 0x86, 0x7c, 0x6e, 0x61, 0x2d, 0x77, 0x12, 0x72,
0x0c, 0x61, 0xbe, 0xc9, 0x84, 0xdc, 0x26, 0xb9, 0x88, 0x76, 0x51, 0x3f, 0xb4, 0xfa, 0x4b, 0x87,
0x9f, 0xef, 0xb0, 0x4a, 0x44, 0x0e, 0x21, 0xe0, 0x59, 0xb6, 0x2c, 0xa2, 0x00, 0xd5, 0xff, 0x59,
0xf5, 0x54, 0x63, 0xe7, 0x3b, 0xcc, 0x90, 0x64, 0x00, 0x1d, 0x55, 0x44, 0xdd, 0xb1, 0x37, 0x09,
0x58, 0x47, 0x15, 0xb3, 0x1e, 0x04, 0x5b, 0xbe, 0xdc, 0x08, 0x3a, 0x81, 0xbe, 0xbb, 0x26, 0x79,
0x06, 0xa1, 0xad, 0xe9, 0xc5, 0x19, 0x96, 0x22, 0x64, 0x15, 0x40, 0xdf, 0x6b, 0xa5, 0xbd, 0xd5,
0x3f, 0x95, 0x84, 0xc0, 0x2e, 0x5f, 0x2c, 0x24, 0x96, 0x27, 0x64, 0xf8, 0x4d, 0xdf, 0x40, 0x80,
0x29, 0x3d, 0x70, 0x74, 0x00, 0x9d, 0x34, 0xc3, 0x83, 0x01, 0xeb, 0xa4, 0x19, 0x7d, 0x0b, 0x01,
0xcf, 0x73, 0xa1, 0xc8, 0x01, 0x74, 0xc5, 0xad, 0x88, 0x6d, 0x8f, 0x42, 0x66, 0x2d, 0x8d, 0xe7,
0xc5, 0xea, 0x2a, 0x5d, 0xda, 0x68, 0xd6, 0xa2, 0x3f, 0x3d, 0xe8, 0xbb, 0x0a, 0x13, 0x0a, 0xc1,
0x54, 0x7b, 0xb1, 0xfd, 0x75, 0x35, 0x42, 0xcf, 0xcc, 0x50, 0xe4, 0x10, 0xfe, 0xbf, 0x96, 0xe9,
0x6a, 0x5a, 0xe6, 0x66, 0xfc, 0x35, 0x41, 0x32, 0x86, 0x3d, 0x95, 0x56, 0x1a, 0x1f, 0x35, 0x75,
0x48, 0x27, 0xc4, 0x57, 0xfa, 0x1b, 0xdb, 0xe7, 0x33, 0x6b, 0xd1, 0x53, 0x08, 0xcb, 0x0e, 0x92,
0xe7, 0x00, 0xe5, 0x9d, 0xf3, 0xc8, 0x1b, 0xfb, 0x93, 0x90, 0xd5, 0x90, 0x3b, 0x65, 0xf8, 0xed,
0x41, 0xcf, 0xd2, 0x8f, 0xaf, 0x3d, 0x79, 0x0a, 0xfd, 0x4c, 0x8a, 0xed, 0x54, 0xe3, 0x26, 0xe3,
0xd2, 0xc6, 0xfa, 0x29, 0xae, 0x36, 0x39, 0xa6, 0x1b, 0x30, 0x6b, 0xe9, 0x0c, 0x63, 0x29, 0xb8,
0x12, 0xf3, 0x64, 0x25, 0x70, 0xb6, 0x7c, 0x56, 0x43, 0x34, 0x2f, 0x6e, 0xb3, 0x44, 0x1a, 0xbe,
0x6b, 0xf8, 0x0a, 0xd1, 0x31, 0x97, 0x69, 0xfc, 0x0d, 0xd9, 0x1e, 0xb2, 0xa5, 0x4d, 0x46, 0x10,
0x24, 0xeb, 0x85, 0xb8, 0x8d, 0xfa, 0x48, 0x18, 0x83, 0xbe, 0x2b, 0x9f, 0x2c, 0x13, 0xb1, 0x48,
0x32, 0x45, 0x26, 0xe5, 0xa5, 0x6d, 0xe3, 0x06, 0xae, 0x71, 0x56, 0xe7, 0x68, 0xfa, 0x15, 0x86,
0x4c, 0x64, 0xcb, 0xc2, 0x3a, 0xf8, 0x9c, 0xe4, 0x8a, 0xbc, 0x80, 0xbe, 0xa5, 0x4d, 0x85, 0xef,
0x1e, 0x2f, 0x79, 0x7d, 0x9b, 0x4c, 0x26, 0x2b, 0x2e, 0x0b, 0xf7, 0xac, 0x43, 0x56, 0x43, 0xe8,
0x0f, 0x0f, 0xf6, 0xdd, 0x34, 0xb9, 0xec, 0x8e, 0x61, 0xef, 0x63, 0x35, 0x1b, 0xf7, 0x64, 0x58,
0x97, 0x90, 0x97, 0x10, 0xce, 0xdd, 0xa4, 0xd8, 0xdd, 0xd1, 0xd6, 0x57, 0x82, 0xaa, 0x4a, 0x7e,
0xbd, 0x4a, 0x0b, 0x18, 0x96, 0x63, 0xe4, 0x32, 0x79, 0xcc, 0x4d, 0x5b, 0x93, 0x75, 0x4f, 0x94,
0x2d, 0x8c, 0xbe, 0x6c, 0x84, 0x2c, 0x3e, 0x60, 0x43, 0x17, 0xd3, 0xbf, 0xd7, 0xc9, 0x6b, 0xd7,
0x49, 0x3f, 0x0f, 0x33, 0x03, 0x0b, 0x6c, 0x7c, 0x07, 0x7d, 0xd6, 0x21, 0x3d, 0xbd, 0x8b, 0x44,
0x0a, 0x5c, 0xbd, 0x18, 0x33, 0x60, 0x15, 0x40, 0x57, 0xf0, 0x04, 0xe3, 0xba, 0x80, 0xb3, 0xe2,
0xd2, 0x8c, 0x63, 0x35, 0xa6, 0x5e, 0x7b, 0x4c, 0x6b, 0x09, 0xf9, 0x77, 0x12, 0x6a, 0x84, 0x0b,
0xda, 0xe1, 0x8e, 0x61, 0x58, 0x0f, 0x37, 0x2b, 0x2e, 0xce, 0x1e, 0x58, 0x82, 0x13, 0x20, 0xcd,
0x13, 0xf8, 0x88, 0xdc, 0xa3, 0xf3, 0x6a, 0x0b, 0x6f, 0x6e, 0x7d, 0xcf, 0xf8, 0x92, 0xaf, 0x63,
0xf1, 0xb0, 0x6f, 0xbd, 0xa5, 0x70, 0x23, 0xd9, 0xd1, 0x68, 0x6d, 0x29, 0xfc, 0xa1, 0xa7, 0xd0,
0xbb, 0x32, 0x0e, 0x49, 0x54, 0x7e, 0xa2, 0x2b, 0x9f, 0x95, 0xcc, 0x01, 0x74, 0xaf, 0x65, 0xfa,
0x5d, 0xac, 0x6d, 0x03, 0xac, 0x75, 0x32, 0x84, 0x41, 0xf3, 0x3f, 0xf3, 0xaa, 0x8b, 0x7f, 0x9a,
0xaf, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x71, 0xbc, 0x47, 0xe4, 0x4c, 0x07, 0x00, 0x00,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment