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
import (
"time"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
......@@ -10,11 +12,11 @@ import (
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"time"
"testing"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"github.com/stretchr/testify/assert"
"testing"
)
type execEnv struct {
......@@ -110,10 +112,13 @@ func TestAccountManager(t *testing.T) {
//注册
tx1, err := CreateRegister(&et.Register{AccountID: "harrylee2015"}, PrivKeyB)
if err != nil {
t.Error(err)
}
Exec_Block(t, stateDB, kvdb, env, tx1)
assert.Equal(t, err, nil)
err = Exec_Block(t, stateDB, kvdb, env, tx1)
assert.Equal(t, err, nil)
_, 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)
err = Exec_Block(t, stateDB, kvdb, env, tx2)
assert.Equal(t, err, et.ErrAccountIDExist)
......@@ -181,12 +186,31 @@ func TestAccountManager(t *testing.T) {
//过期账户查询
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)
}
t.Log(accs)
accounts, err = Exec_QueryExpiredAccounts(time.Now().Unix(), stateDB, kvdb)
assert.Equal(t, err, nil)
assert.Equal(t, accounts.Accounts[0].AccountID, "harrylee2015")
//账户延期
tx10, _ := CreateSupervise(&et.Supervise{
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) {
......@@ -290,6 +314,12 @@ func Exec_Block(t *testing.T, stateDB db.DB, kvdb db.KVDB, env *execEnv, txs ...
cfg.SetTitleOnlyForTest("chain33")
exec := newAccountmanager()
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 {
err := e.CheckTx(tx, index)
if err != nil {
......@@ -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.blockTime = env.blockTime + 1
env.difficulty = env.difficulty + 1
......
......@@ -2,6 +2,9 @@ package executor
import (
"fmt"
"strconv"
"time"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
dbm "github.com/33cn/chain33/common/db"
......@@ -9,8 +12,6 @@ import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"strconv"
"time"
)
var (
......@@ -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()}
}
//GetIndex get index 主键索引
//GetIndex get index 主键索引,实际上是以过期时间为主键
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
......@@ -74,6 +75,7 @@ func (a *Action) Register(payload *et.Register) (*types.Receipt, error) {
Addr: a.fromaddr,
PrevAddr: "",
Status: et.Normal,
Level: et.Normal,
CreateTime: a.blocktime,
ExpireTime: a.blocktime + defaultActiveTime,
LockTime: 0,
......@@ -237,6 +239,8 @@ func (a *Action) Supervise(payload *et.Supervise) (*types.Receipt, error) {
defaultActiveTime := getConfValue(cfg, a.statedb, ConfNameActiveTime, DefaultActiveTime)
accountM.Status = et.Normal
accountM.ExpireTime = a.blocktime + defaultActiveTime
case et.Authorize:
accountM.Level = payload.Level
}
re.Accounts = append(re.Accounts, accountM)
}
......@@ -407,7 +411,7 @@ func findAccountByID(localdb dbm.KV, accountID string) (*et.Account, error) {
//第一次查询,默认展示最新得成交记录
rows, err := table.ListIndex("accountID", prefix, nil, 1, et.ListDESC)
if err != nil {
elog.Error("findAccountByID.", "prefix", prefix, "err", err.Error())
elog.Debug("findAccountByID.", "accountID", accountID, "err", err.Error())
return nil, err
}
for _, row := range rows {
......
......@@ -4,7 +4,7 @@ import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"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 (
elog = log.New("module", "accountmanager.executor")
)
var driverName = accountmanagertypes.AccountmanagerX
var driverName = et.AccountmanagerX
// Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) {
......@@ -58,6 +58,31 @@ func (e *accountmanager) ExecutorOrder() int64 {
// CheckTx 实现自定义检验交易接口,供框架调用
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
}
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
import (
"fmt"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
......@@ -74,7 +75,7 @@ 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("%018d", m.GetIndex())), nil
return []byte(fmt.Sprintf("%015d", m.GetIndex())), nil
} else if key == "addr" {
return []byte(fmt.Sprintf("%s", m.GetAddr())), nil
}
......
package executor
import (
"testing"
"time"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
et "github.com/33cn/plugin/plugin/dapp/accountmanager/types"
"testing"
"time"
)
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 := 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 {
kvs, err := table.Save()
if err != nil {
t.Error(err)
}
for _,kv :=range kvs{
kvdb.Set(kv.Key,kv.Value)
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 {
time.Sleep(2 * time.Second)
list, err := findAccountListByIndex(kvdb, time.Now().Unix()+10, "")
if err != nil {
t.Error(err)
}
t.Log(list)
list,err=findAccountListByStatus(kvdb,et.Normal,0,"")
if err !=nil {
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 {
row1.Status = et.Frozen
err = table.Replace(row1)
if err != nil {
t.Error(err)
}
kvs,err=table.Save()
if err !=nil {
kvs, err = table.Save()
if err != nil {
t.Error(err)
}
for _,kv :=range kvs{
kvdb.Set(kv.Key,kv.Value)
for _, kv := range kvs {
kvdb.Set(kv.Key, kv.Value)
}
list,err=findAccountListByStatus(kvdb,et.Frozen,0,"")
if err !=nil {
list, err = findAccountListByStatus(kvdb, et.Frozen, 0, "")
if err != nil {
t.Error(err)
}
t.Log(list)
}
\ No newline at end of file
}
......@@ -64,8 +64,10 @@ message Transfer {
message Supervise {
//账户名单
repeated string accountIDs = 1;
//操作, 1为冻结,2为解冻,3增加有效期
//操作, 1为冻结,2为解冻,3增加有效期,4为授权
int32 op = 2;
//0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
int32 level = 3;
}
message account{
......@@ -75,16 +77,18 @@ message account{
string addr = 2;
//上一次公钥地址
string prevAddr = 3;
//账户状态 1 正常, 2表示冻结, 3表示锁定 4,过期注销
//账户状态 0 正常, 1表示冻结, 2表示锁定 3,过期注销
int32 status = 4;
//等级权限 0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
int32 level = 5;
//注册时间
int64 createTime = 5;
int64 createTime = 6;
//失效时间
int64 expireTime = 6;
int64 expireTime = 7;
//锁定时间
int64 lockTime = 7;
//索引
int64 index = 8;
int64 lockTime = 8;
//主键索引
int64 index = 9;
}
message AccountReceipt{
......
package types
import (
"reflect"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
"reflect"
)
/*
......@@ -46,8 +47,7 @@ const (
//状态
const (
UnknownStatus = int32(iota)
Normal
Normal = int32(iota)
Frozen
Locked
Expired
......@@ -59,6 +59,7 @@ const (
Freeze
UnFreeze
AddExpire
Authorize
)
//apply op
......
......@@ -422,8 +422,10 @@ func (m *Transfer) GetAmount() int64 {
type Supervise struct {
// 账户名单
AccountIDs []string `protobuf:"bytes,1,rep,name=accountIDs" json:"accountIDs,omitempty"`
// 操作, 1为冻结,2为解冻,3增加有效期
// 操作, 1为冻结,2为解冻,3增加有效期,4为授权
Op int32 `protobuf:"varint,2,opt,name=op" json:"op,omitempty"`
// 0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
Level int32 `protobuf:"varint,3,opt,name=level" json:"level,omitempty"`
}
func (m *Supervise) Reset() { *m = Supervise{} }
......@@ -445,6 +447,13 @@ func (m *Supervise) GetOp() int32 {
return 0
}
func (m *Supervise) GetLevel() int32 {
if m != nil {
return m.Level
}
return 0
}
type Account struct {
// 账户名称
AccountID string `protobuf:"bytes,1,opt,name=accountID" json:"accountID,omitempty"`
......@@ -452,16 +461,18 @@ type Account struct {
Addr string `protobuf:"bytes,2,opt,name=addr" json:"addr,omitempty"`
// 上一次公钥地址
PrevAddr string `protobuf:"bytes,3,opt,name=prevAddr" json:"prevAddr,omitempty"`
// 账户状态 1 正常, 2表示冻结, 3表示锁定 4,过期注销
// 账户状态 0 正常, 1表示冻结, 2表示锁定 3,过期注销
Status int32 `protobuf:"varint,4,opt,name=status" json:"status,omitempty"`
// 等级权限 0普通,后面根据业务需要可以自定义,有管理员授予不同的权限
Level int32 `protobuf:"varint,5,opt,name=level" json:"level,omitempty"`
// 注册时间
CreateTime int64 `protobuf:"varint,5,opt,name=createTime" json:"createTime,omitempty"`
CreateTime int64 `protobuf:"varint,6,opt,name=createTime" json:"createTime,omitempty"`
// 失效时间
ExpireTime int64 `protobuf:"varint,6,opt,name=expireTime" json:"expireTime,omitempty"`
ExpireTime int64 `protobuf:"varint,7,opt,name=expireTime" json:"expireTime,omitempty"`
// 锁定时间
LockTime int64 `protobuf:"varint,7,opt,name=lockTime" json:"lockTime,omitempty"`
// 索引
Index int64 `protobuf:"varint,8,opt,name=index" json:"index,omitempty"`
LockTime int64 `protobuf:"varint,8,opt,name=lockTime" json:"lockTime,omitempty"`
// 主键索引
Index int64 `protobuf:"varint,9,opt,name=index" json:"index,omitempty"`
}
func (m *Account) Reset() { *m = Account{} }
......@@ -497,6 +508,13 @@ func (m *Account) GetStatus() int32 {
return 0
}
func (m *Account) GetLevel() int32 {
if m != nil {
return m.Level
}
return 0
}
func (m *Account) GetCreateTime() int64 {
if m != nil {
return m.CreateTime
......@@ -843,50 +861,51 @@ var _Accountmanager_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("accountmanager.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 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,
// 731 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4d, 0x6b, 0xdb, 0x4c,
0x10, 0x8e, 0xac, 0xc8, 0xb6, 0x26, 0xef, 0xeb, 0x98, 0xc5, 0x0d, 0xa2, 0x94, 0x62, 0x96, 0x1c,
0x4c, 0x69, 0x43, 0x48, 0x29, 0x85, 0xb6, 0x17, 0x9b, 0xb4, 0x24, 0xb4, 0x97, 0x6c, 0x7c, 0x2e,
0x28, 0xf2, 0x24, 0x88, 0xca, 0x96, 0x58, 0xad, 0x4d, 0xd4, 0x3f, 0xd0, 0x5b, 0x4f, 0xfd, 0xaf,
0xbd, 0x96, 0x9d, 0x5d, 0x7d, 0x39, 0x69, 0x43, 0x4e, 0xda, 0x79, 0xe6, 0xd9, 0xf9, 0xde, 0x11,
0x8c, 0xc2, 0x28, 0x4a, 0xd7, 0x2b, 0xb5, 0x0c, 0x57, 0xe1, 0x0d, 0xca, 0xa3, 0x4c, 0xa6, 0x2a,
0x65, 0x9e, 0x2a, 0x32, 0xcc, 0xf9, 0x10, 0x06, 0xd3, 0x96, 0x9a, 0xff, 0xea, 0xc0, 0xa8, 0x0d,
0x4d, 0x23, 0x15, 0xa7, 0x2b, 0xf6, 0x0a, 0xfa, 0x12, 0x6f, 0xe2, 0x5c, 0xa1, 0x0c, 0x9c, 0xb1,
0x33, 0xd9, 0x3b, 0xd9, 0x3f, 0x22, 0x23, 0x47, 0xc2, 0xc2, 0x67, 0x3b, 0xa2, 0xa2, 0x18, 0x7a,
0x8e, 0xea, 0x33, 0x16, 0x41, 0x67, 0x8b, 0x6e, 0x60, 0x43, 0x37, 0x67, 0x4d, 0x57, 0x32, 0x5c,
0xe5, 0xd7, 0x28, 0x03, 0xb7, 0x45, 0x9f, 0x5b, 0x58, 0xd3, 0x4b, 0x0a, 0x3b, 0x06, 0x3f, 0x5f,
0x67, 0x28, 0x37, 0x71, 0x8e, 0xc1, 0x2e, 0xf1, 0x87, 0x96, 0x7f, 0x59, 0xe2, 0x67, 0x3b, 0xa2,
0x26, 0xb1, 0x43, 0xf0, 0xc2, 0x2c, 0x4b, 0x8a, 0xc0, 0x23, 0xf6, 0x7f, 0x96, 0x3d, 0xd5, 0xd8,
0xd9, 0x8e, 0x30, 0x4a, 0x36, 0x80, 0x8e, 0x2a, 0x82, 0xee, 0xd8, 0x99, 0x78, 0xa2, 0xa3, 0x8a,
0x59, 0x0f, 0xbc, 0x4d, 0x98, 0xac, 0x91, 0x4f, 0xa0, 0x5f, 0xa6, 0xc9, 0x9e, 0x81, 0x6f, 0x6b,
0x7a, 0x7e, 0x4a, 0xa5, 0xf0, 0x45, 0x0d, 0xf0, 0x0f, 0x9a, 0x69, 0xb3, 0xfa, 0x27, 0x93, 0x31,
0xd8, 0x0d, 0x17, 0x0b, 0x49, 0xe5, 0xf1, 0x05, 0x9d, 0xf9, 0x1b, 0xf0, 0x28, 0xa4, 0x07, 0xae,
0x0e, 0xa0, 0x93, 0x66, 0x74, 0xd1, 0x13, 0x9d, 0x34, 0xe3, 0x6f, 0xc1, 0x0b, 0xf3, 0x1c, 0x15,
0x3b, 0x80, 0x2e, 0xde, 0x62, 0x64, 0x7b, 0xe4, 0x0b, 0x2b, 0x69, 0x3c, 0x2f, 0x96, 0x57, 0x69,
0x62, 0xbd, 0x59, 0x89, 0xff, 0x74, 0xa0, 0x5f, 0x56, 0x98, 0x71, 0xf0, 0xa6, 0xda, 0x8a, 0xed,
0x6f, 0x59, 0x23, 0xb2, 0x2c, 0x8c, 0x8a, 0x1d, 0xc2, 0xff, 0xd7, 0x32, 0x5d, 0x4e, 0xab, 0xd8,
0x8c, 0xbd, 0x36, 0xc8, 0xc6, 0xb0, 0xa7, 0xd2, 0x9a, 0xe3, 0x12, 0xa7, 0x09, 0xe9, 0x80, 0xc2,
0xa5, 0x3e, 0x53, 0xfb, 0x5c, 0x61, 0x25, 0x7e, 0x01, 0x7e, 0xd5, 0x41, 0xf6, 0x1c, 0xa0, 0xca,
0x39, 0x0f, 0x9c, 0xb1, 0x3b, 0xf1, 0x45, 0x03, 0xd9, 0x2e, 0x03, 0x1b, 0x81, 0x97, 0xe0, 0x06,
0x13, 0x72, 0xe8, 0x09, 0x23, 0xf0, 0xdf, 0x0e, 0xf4, 0xec, 0xa5, 0xc7, 0x77, 0x84, 0x3d, 0x85,
0x7e, 0x26, 0x71, 0x33, 0xd5, 0xb8, 0xc9, 0xa3, 0x92, 0xa9, 0xaa, 0x2a, 0x54, 0xeb, 0x9c, 0x92,
0xf0, 0x84, 0x95, 0xea, 0x38, 0xbc, 0x46, 0x1c, 0x3a, 0x9b, 0x48, 0x62, 0xa8, 0x70, 0x1e, 0x2f,
0x91, 0x86, 0xcc, 0x15, 0x0d, 0x44, 0xeb, 0xf1, 0x36, 0x8b, 0xa5, 0xd1, 0xf7, 0x8c, 0xbe, 0x46,
0x74, 0x24, 0x49, 0x1a, 0x7d, 0x23, 0x6d, 0x9f, 0xb4, 0x95, 0xac, 0x3d, 0xc6, 0xab, 0x05, 0xde,
0x06, 0x3e, 0x29, 0x8c, 0xc0, 0xdf, 0x55, 0xcf, 0x5b, 0x60, 0x84, 0x71, 0xa6, 0xd8, 0xa4, 0x2a,
0x85, 0x6d, 0xf2, 0xa0, 0x6c, 0xb2, 0xe5, 0x95, 0x6a, 0xfe, 0x15, 0x86, 0x02, 0xb3, 0xa4, 0xb0,
0x06, 0xbe, 0xc4, 0xb9, 0x62, 0x2f, 0xa0, 0x6f, 0xd5, 0xa6, 0x1b, 0x77, 0xaf, 0x57, 0x7a, 0x9d,
0x4d, 0x26, 0xe3, 0x65, 0x28, 0x8b, 0x72, 0x05, 0xf8, 0xa2, 0x81, 0xf0, 0x1f, 0x0e, 0xec, 0x97,
0x93, 0x57, 0x46, 0x77, 0x0c, 0x7b, 0x9f, 0xea, 0x39, 0xfa, 0x4b, 0x84, 0x4d, 0x0a, 0x7b, 0x09,
0xfe, 0xbc, 0x9c, 0x2a, 0xbb, 0x67, 0xb6, 0xf9, 0x35, 0xa1, 0xae, 0x92, 0xdb, 0xac, 0xd2, 0x02,
0x86, 0xd5, 0xc8, 0x95, 0x91, 0x3c, 0x26, 0xd3, 0x7b, 0xa6, 0xf0, 0x1e, 0x2f, 0x1b, 0x18, 0x5d,
0xac, 0x51, 0x16, 0x1f, 0xa9, 0xa1, 0x8b, 0xe9, 0xfd, 0x75, 0x72, 0xb6, 0xeb, 0xa4, 0x9f, 0x92,
0x99, 0x81, 0x05, 0x35, 0xbe, 0x43, 0x36, 0x9b, 0x90, 0x9e, 0xe9, 0x45, 0x2c, 0x91, 0xd6, 0xb4,
0x9d, 0xfc, 0x1a, 0xe0, 0x4b, 0x78, 0x42, 0x7e, 0x4b, 0x87, 0xb3, 0xe2, 0xd2, 0x0c, 0x69, 0x3d,
0xbc, 0x4e, 0x6b, 0x78, 0xdb, 0x01, 0xb9, 0x77, 0x02, 0x6a, 0xb9, 0xf3, 0xb6, 0xdd, 0x1d, 0xc3,
0xb0, 0xe9, 0x6e, 0x56, 0x9c, 0x9f, 0x3e, 0xb0, 0x30, 0x27, 0xc0, 0xda, 0x37, 0xe8, 0x69, 0x95,
0x4f, 0xd1, 0x69, 0x2c, 0xc7, 0xb9, 0xb5, 0x3d, 0x0b, 0x93, 0x70, 0x15, 0xe1, 0xc3, 0xb6, 0xf5,
0x46, 0xa3, 0xed, 0x65, 0x47, 0x63, 0x6b, 0xa3, 0xd1, 0x87, 0xbf, 0x87, 0xde, 0x95, 0x31, 0xc8,
0x82, 0xea, 0x48, 0xa6, 0x5c, 0x51, 0x69, 0x0e, 0xa0, 0x7b, 0x2d, 0xd3, 0xef, 0xb8, 0xb2, 0x0d,
0xb0, 0xd2, 0xc9, 0x10, 0x06, 0xed, 0xff, 0xeb, 0x55, 0x97, 0x7e, 0xb0, 0xaf, 0xff, 0x04, 0x00,
0x00, 0xff, 0xff, 0x84, 0x94, 0xab, 0xf2, 0x78, 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