Commit 235f4bb4 authored by harrylee's avatar harrylee Committed by vipwzw

goimports files

parent 2f39ff09
# accountmanager合约
## 前言
为适配央行发布的[金融分布式账户技术安全规范](http://www.cfstc.org/bzgk/gk/view/yulan.jsp?i_id=1855),满足联盟链中的金融监管,账户公钥重置,黑白名单等要求,特意在chain33上面开发了
accountmanager合约
## 使用
合约按照中心化金融服务的设计,有管理员对合约下面的账户进行监管
提供如下功能:
功能|内容
----|----
账户创建|普通账户,管理员账户,其他有特殊权限的系统账户,在accountmanager合约中,accountID具有唯一性,可作为身份的标识
账户授权|普通权限在注册时即以授权,特殊权限则需要管理员进行授权
账户冻结和解冻|账户冻结由管理员发起,冻结的账户不能交易,账户下的资产将会被冻结
账户锁定和恢复|用于当私钥遗失,重置外部私钥的情况,需要有一定的锁定期限,在锁定期内不能转移账户下的资产
账户注销| 账户应设使用期限,默认五年时间,过期账户将被注销,提供已注销账户的查询接口
账户资产|账户资产可在accountmanager合约下进行正常的流转
合约接口,在线构造交易和查询接口分别复用了框架中的CreateTransaction和Query接口,详情请参考
[CreateTransaction接口](https://github.com/33cn/chain33/blob/master/rpc/jrpchandler.go#L1101)[Query接口](https://github.com/33cn/chain33/blob/master/rpc/jrpchandler.go#L838)
查询方法名称|功能
-----|----
QueryAccountByID|根据账户ID查询账户信息,可用于检查账户ID是否注册
QueryAccountsByStatus|根据状态查询账户信息
QueryExpiredAccounts|查询过期时间
QueryAccountByAddr|根据用户地址查询账户信息
QueryBalanceByID|根据账户ID查询账户资产余额
可参照account_test.go中得相关测试用例,构建相关交易进行测试
## 注意事项
**表结构说明**
表名|主键|索引|用途|说明
---|---|---|---|---
account|index|accountID,addr,status|记录注册账户信息|index是复合索引由{expiretime*1e5+index(注册交易所在区块中的索引)}构成
**表中相关参数说明**
参数名|说明
----|----
Asset|资产名称
op|操作类型 分为supervisor op 1为冻结,2为解冻,3增加有效期,4为授权 apply op 1 撤销账户公钥重置, 2 锁定期结束后,执行重置公钥操作
status|账户状态,0 正常, 1表示冻结, 2表示锁定 3,过期注销
level|账户权限 0 普通,其他根据业务需求自定义
index|账户逾期的时间戳*1e5+注册交易在区块中的索引,占位15 %015d
File mode changed from 100644 to 100755
package executor package executor
import ( import (
"time"
"github.com/33cn/chain33/account" "github.com/33cn/chain33/account"
"github.com/33cn/chain33/client" "github.com/33cn/chain33/client"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
...@@ -10,11 +12,11 @@ import ( ...@@ -10,11 +12,11 @@ import (
"github.com/33cn/chain33/queue" "github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/chain33/util" "github.com/33cn/chain33/util"
"time"
"testing"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types" et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing"
) )
type execEnv struct { type execEnv struct {
...@@ -110,10 +112,13 @@ func TestAccountManager(t *testing.T) { ...@@ -110,10 +112,13 @@ func TestAccountManager(t *testing.T) {
//注册 //注册
tx1, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyB) tx1, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyB)
if err != nil { assert.Equal(t, err, nil)
t.Error(err) err = Exec_Block(t, stateDB, kvdb, env, tx1)
} assert.Equal(t, err, nil)
Exec_Block(t, stateDB, kvdb, env, tx1) _, err = Exec_QueryAccountByID("harrylee2015", stateDB, kvdb)
assert.Equal(t, err, nil)
_, err = Exec_QueryAccountByAddr(Nodes[1], stateDB, kvdb)
assert.Equal(t, err, nil)
tx2, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyC) tx2, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyC)
err = Exec_Block(t, stateDB, kvdb, env, tx2) err = Exec_Block(t, stateDB, kvdb, env, tx2)
assert.Equal(t, err, et.ErrAccountIDExist) assert.Equal(t, err, et.ErrAccountIDExist)
...@@ -181,12 +186,31 @@ func TestAccountManager(t *testing.T) { ...@@ -181,12 +186,31 @@ func TestAccountManager(t *testing.T) {
//过期账户查询 //过期账户查询
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
t.Log(time.Now().Unix()) accounts, err = Exec_QueryExpiredAccounts(time.Now().Unix(), stateDB, kvdb)
accs, err := Exec_QueryExpiredAccounts(time.Now().Unix(), stateDB, kvdb) assert.Equal(t, err, nil)
if err != nil { assert.Equal(t, accounts.Accounts[0].AccountID, "harrylee2015")
t.Error(err) //账户延期
} tx10, _ := CreateSupervise(&et.Supervise{
t.Log(accs) AccountIDs: []string{"harrylee2015"},
Op: et.AddExpire,
}, PrivKeyA)
err = Exec_Block(t, stateDB, kvdb, env, tx10)
assert.Equal(t, err, nil)
accounts, err = Exec_QueryExpiredAccounts(time.Now().Unix(), stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, len(accounts.Accounts), 0)
//账户授权
tx11, _ := CreateSupervise(&et.Supervise{
AccountIDs: []string{"harrylee2015"},
Op: et.Authorize,
Level: 2,
}, PrivKeyA)
err = Exec_Block(t, stateDB, kvdb, env, tx11)
assert.Equal(t, err, nil)
acc, err := Exec_QueryAccountByID("harrylee2015", stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, acc.Level, int32(2))
} }
func CreateRegister(register *et.Register, privKey string) (tx *types.Transaction, err error) { func CreateRegister(register *et.Register, privKey string) (tx *types.Transaction, err error) {
...@@ -290,6 +314,12 @@ func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ... ...@@ -290,6 +314,12 @@ func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ...
cfg.SetTitleOnlyForTest("chain33") cfg.SetTitleOnlyForTest("chain33")
exec := newAccountmanager() exec := newAccountmanager()
e := exec.(*accountmanager) e := exec.(*accountmanager)
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
for index, tx := range txs { for index, tx := range txs {
err := e.CheckTx(tx, index) err := e.CheckTx(tx, index)
if err != nil { if err != nil {
...@@ -297,12 +327,6 @@ func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ... ...@@ -297,12 +327,6 @@ func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ...
} }
} }
q := queue.New("channel")
q.SetConfig(cfg)
api, _ := client.New(q.Client(), nil)
exec.SetAPI(api)
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
env.blockHeight = env.blockHeight + 1 env.blockHeight = env.blockHeight + 1
env.blockTime = env.blockTime + 1 env.blockTime = env.blockTime + 1
env.difficulty = env.difficulty + 1 env.difficulty = env.difficulty + 1
......
...@@ -2,6 +2,9 @@ package executor ...@@ -2,6 +2,9 @@ package executor
import ( import (
"fmt" "fmt"
"strconv"
"time"
"github.com/33cn/chain33/account" "github.com/33cn/chain33/account"
"github.com/33cn/chain33/client" "github.com/33cn/chain33/client"
dbm "github.com/33cn/chain33/common/db" dbm "github.com/33cn/chain33/common/db"
...@@ -9,8 +12,6 @@ import ( ...@@ -9,8 +12,6 @@ import (
"github.com/33cn/chain33/system/dapp" "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types" et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"strconv"
"time"
) )
var ( var (
...@@ -48,9 +49,9 @@ func NewAction(e *accountmanager, tx *types.Transaction, index int) *Action { ...@@ -48,9 +49,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()} 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 { func (a *Action) GetIndex() int64 {
return a.height*types.MaxTxsPerBlock + int64(a.index) return a.blocktime*types.MaxTxsPerBlock + int64(a.index)
} }
//GetKVSet get kv set //GetKVSet get kv set
...@@ -74,6 +75,7 @@ func (a *Action) Register(payload *et.Register) (*types.Receipt, error) { ...@@ -74,6 +75,7 @@ func (a *Action) Register(payload *et.Register) (*types.Receipt, error) {
Addr: a.fromaddr, Addr: a.fromaddr,
PrevAddr: "", PrevAddr: "",
Status: et.Normal, Status: et.Normal,
Level: et.Normal,
CreateTime: a.blocktime, CreateTime: a.blocktime,
ExpireTime: a.blocktime + defaultActiveTime, ExpireTime: a.blocktime + defaultActiveTime,
LockTime: 0, LockTime: 0,
...@@ -237,6 +239,8 @@ func (a *Action) Supervise(payload *et.Supervise) (*types.Receipt, error) { ...@@ -237,6 +239,8 @@ func (a *Action) Supervise(payload *et.Supervise) (*types.Receipt, error) {
defaultActiveTime := getConfValue(cfg, a.statedb, ConfNameActiveTime, DefaultActiveTime) defaultActiveTime := getConfValue(cfg, a.statedb, ConfNameActiveTime, DefaultActiveTime)
accountM.Status = et.Normal accountM.Status = et.Normal
accountM.ExpireTime = a.blocktime + defaultActiveTime accountM.ExpireTime = a.blocktime + defaultActiveTime
case et.Authorize:
accountM.Level = payload.Level
} }
re.Accounts = append(re.Accounts, accountM) re.Accounts = append(re.Accounts, accountM)
} }
...@@ -407,7 +411,7 @@ func findAccountByID(localdb dbm.KV, accountID string) (*et.Account, error) { ...@@ -407,7 +411,7 @@ func findAccountByID(localdb dbm.KV, accountID string) (*et.Account, error) {
//第一次查询,默认展示最新得成交记录 //第一次查询,默认展示最新得成交记录
rows, err := table.ListIndex("accountID", prefix, nil, 1, et.ListDESC) rows, err := table.ListIndex("accountID", prefix, nil, 1, et.ListDESC)
if err != nil { if err != nil {
elog.Error("findAccountByID.", "prefix", prefix, "err", err.Error()) elog.Debug("findAccountByID.", "accountID", accountID, "err", err.Error())
return nil, err return nil, err
} }
for _, row := range rows { for _, row := range rows {
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
log "github.com/33cn/chain33/common/log/log15" log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp" drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
accountmanagertypes "github.com/33cn/plugin/plugin/dapp/accountmanager/types" et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
) )
/* /*
...@@ -17,7 +17,7 @@ var ( ...@@ -17,7 +17,7 @@ var (
elog = log.New("module", "accountmanager.executor") elog = log.New("module", "accountmanager.executor")
) )
var driverName = accountmanagertypes.AccountmanagerX var driverName = et.AccountmanagerX
// Init register dapp // Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) { func Init(name string, cfg *types.Chain33Config, sub []byte) {
...@@ -58,6 +58,31 @@ func (e *accountmanager) ExecutorOrder() int64 { ...@@ -58,6 +58,31 @@ func (e *accountmanager) ExecutorOrder() int64 {
// CheckTx 实现自定义检验交易接口,供框架调用 // CheckTx 实现自定义检验交易接口,供框架调用
func (a *accountmanager) CheckTx(tx *types.Transaction, index int) error { func (a *accountmanager) CheckTx(tx *types.Transaction, index int) error {
// implement code //发送交易的时候就检查payload,做严格的参数检查
var ama et.AccountmanagerAction
types.Decode(tx.GetPayload(), &ama)
switch ama.Ty {
case et.TyRegisterAction:
register := ama.GetRegister()
if a.CheckAccountIDIsExist(register.GetAccountID()) {
return et.ErrAccountIDExist
}
case et.TySuperviseAction:
case et.TyApplyAction:
case et.TyTransferAction:
case et.TyResetAction:
}
return nil return nil
} }
func (a *accountmanager) CheckAccountIDIsExist(accountID string) bool {
_, err := findAccountByID(a.GetLocalDB(), accountID)
if err == types.ErrNotFound {
return false
}
return true
}
...@@ -2,6 +2,7 @@ package executor ...@@ -2,6 +2,7 @@ package executor
import ( import (
"fmt" "fmt"
"github.com/33cn/chain33/common/db" "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table" "github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
...@@ -74,7 +75,7 @@ func (m *AccountRow) Get(key string) ([]byte, error) { ...@@ -74,7 +75,7 @@ func (m *AccountRow) Get(key string) ([]byte, error) {
} else if key == "status" { } else if key == "status" {
return []byte(fmt.Sprintf("%d", m.Status)), nil return []byte(fmt.Sprintf("%d", m.Status)), nil
} else if key == "index" { } else if key == "index" {
return []byte(fmt.Sprintf("%018d", m.GetIndex())), nil return []byte(fmt.Sprintf("%015d", m.GetIndex())), nil
} else if key == "addr" { } else if key == "addr" {
return []byte(fmt.Sprintf("%s", m.GetAddr())), nil return []byte(fmt.Sprintf("%s", m.GetAddr())), nil
} }
......
package executor package executor
import ( import (
"testing"
"time"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/chain33/util" "github.com/33cn/chain33/util"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types" et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"testing"
"time"
) )
func TestAccountTable(t *testing.T) { func TestAccountTable(t *testing.T) {
_, _, kvdb := util.CreateTestDB() _, _, kvdb := util.CreateTestDB()
table:=NewAccountTable(kvdb) table := NewAccountTable(kvdb)
now :=time.Now().Unix() now := time.Now().Unix()
row1:= &et.Account{Index:now*int64(types.MaxTxsPerBlock),AccountID:"harry2015",Status:1,ExpireTime:now+10,Addr:"xxxx"} 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"} row2 := &et.Account{Index: now*int64(types.MaxTxsPerBlock) + 1, AccountID: "harry2020", Status: 1, ExpireTime: now, Addr: "xxxx"}
table.Add(row1) table.Add(row1)
table.Add(row2) table.Add(row2)
kvs,err:=table.Save() kvs, err := table.Save()
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
for _,kv :=range kvs{ for _, kv := range kvs {
kvdb.Set(kv.Key,kv.Value) kvdb.Set(kv.Key, kv.Value)
} }
time.Sleep(2*time.Second) time.Sleep(2 * time.Second)
list,err:=findAccountListByIndex(kvdb,time.Now().Unix(),"") list, err := findAccountListByIndex(kvdb, time.Now().Unix()+10, "")
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
t.Log(list) t.Log(list)
list,err=findAccountListByStatus(kvdb,et.Normal,0,"") list, err = findAccountListByStatus(kvdb, et.Normal, 0, "")
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
t.Log(list) t.Log(list)
row1.Status=et.Frozen row1.Status = et.Frozen
err=table.Replace(row1) err = table.Replace(row1)
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
kvs,err=table.Save() kvs, err = table.Save()
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
for _,kv :=range kvs{ for _, kv := range kvs {
kvdb.Set(kv.Key,kv.Value) kvdb.Set(kv.Key, kv.Value)
} }
list,err=findAccountListByStatus(kvdb,et.Frozen,0,"") list, err = findAccountListByStatus(kvdb, et.Frozen, 0, "")
if err !=nil { if err != nil {
t.Error(err) t.Error(err)
} }
t.Log(list) t.Log(list)
} }
\ No newline at end of file
...@@ -64,8 +64,10 @@ message Transfer { ...@@ -64,8 +64,10 @@ message Transfer {
message Supervise { message Supervise {
//账户名单 //账户名单
repeated string accountIDs = 1; repeated string accountIDs = 1;
//操作, 1为冻结,2为解冻,3增加有效期 //操作, 1为冻结,2为解冻,3增加有效期,4为授权
int32 op = 2; int32 op = 2;
//0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
int32 level = 3;
} }
message account{ message account{
...@@ -75,16 +77,18 @@ message account{ ...@@ -75,16 +77,18 @@ message account{
string addr = 2; string addr = 2;
//上一次公钥地址 //上一次公钥地址
string prevAddr = 3; string prevAddr = 3;
//账户状态 1 正常, 2表示冻结, 3表示锁定 4,过期注销 //账户状态 0 正常, 1表示冻结, 2表示锁定 3,过期注销
int32 status = 4; int32 status = 4;
//等级权限 0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
int32 level = 5;
//注册时间 //注册时间
int64 createTime = 5; int64 createTime = 6;
//失效时间 //失效时间
int64 expireTime = 6; int64 expireTime = 7;
//锁定时间 //锁定时间
int64 lockTime = 7; int64 lockTime = 8;
//索引 //主键索引
int64 index = 8; int64 index = 9;
} }
message AccountReceipt{ message AccountReceipt{
......
package types package types
import ( import (
"reflect"
log "github.com/33cn/chain33/common/log/log15" log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"reflect"
) )
/* /*
...@@ -46,8 +47,7 @@ const ( ...@@ -46,8 +47,7 @@ const (
//状态 //状态
const ( const (
UnknownStatus = int32(iota) Normal = int32(iota)
Normal
Frozen Frozen
Locked Locked
Expired Expired
...@@ -59,6 +59,7 @@ const ( ...@@ -59,6 +59,7 @@ const (
Freeze Freeze
UnFreeze UnFreeze
AddExpire AddExpire
Authorize
) )
//apply op //apply op
......
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