Commit 730d0fdc authored by QM's avatar QM

平行链增加监督节点

parent b375d231
......@@ -75,6 +75,7 @@ func getNodes(db dbm.KV, key []byte) (map[string]struct{}, []string, error) {
return nodesMap, nodesArray, nil
}
// manager 合约 分叉前写入 现在不用了
func getConfigManageNodes(db dbm.KV, title string) (map[string]struct{}, []string, error) {
key := calcManageConfigNodesKey(title)
return getNodes(db, key)
......@@ -275,8 +276,7 @@ func getConfigNodes(db dbm.KV, title string) (map[string]struct{}, []string, []b
if errors.Cause(err) != pt.ErrTitleNotExist {
return nil, nil, nil, errors.Wrapf(err, "getNodes para for title:%s", title)
}
key = calcManageConfigNodesKey(title)
nodes, nodesArray, err = getNodes(db, key)
nodes, nodesArray, err = getConfigManageNodes(db, title)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "getNodes manager for title:%s", title)
}
......@@ -287,6 +287,7 @@ func getConfigNodes(db dbm.KV, title string) (map[string]struct{}, []string, []b
func (a *action) getNodesGroup(title string) (map[string]struct{}, []string, error) {
cfg := a.api.GetConfig()
// 如果高度是分叉前,获取老的Nodes
if a.exec.GetMainHeight() < pt.GetDappForkHeight(cfg, pt.ForkCommitTx) {
nodes, nodesArray, err := getConfigManageNodes(a.db, title)
if err != nil {
......@@ -297,7 +298,18 @@ func (a *action) getNodesGroup(title string) (map[string]struct{}, []string, err
nodes, nodesArray, _, err := getConfigNodes(a.db, title)
return nodes, nodesArray, err
}
func (a *action) getSupervisionNodesGroup(title string) (map[string]struct{}, []string, error) {
key := calcParaSupervisionNodeGroupAddrsKey(title)
nodes, nodesArray, err := getNodes(a.db, key)
if err != nil {
if errors.Cause(err) != pt.ErrTitleNotExist {
return nil, nil, errors.Wrapf(err, "getSupervisionNodesGroup para for title:%s", title)
}
}
return nodes, nodesArray, err
}
func (a *action) isValidSuperNode(addr string) error {
......@@ -324,7 +336,6 @@ func updateCommitBlockHashs(stat *pt.ParacrossHeightStatus, commit *pt.Paracross
}
stat.BlockDetails.BlockHashs = append(stat.BlockDetails.BlockHashs, commit.BlockHash)
stat.BlockDetails.TxResults = append(stat.BlockDetails.TxResults, commit.TxResult)
}
//根据nodes过滤掉可能退出了的addrs
......@@ -337,7 +348,17 @@ func updateCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{}
}
}
stat.Details = details
}
func updateSupervisionDetailsCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{}) {
supervisionDetailsDetails := &pt.ParacrossStatusDetails{}
for i, addr := range stat.Details.Addrs {
if _, ok := nodes[addr]; ok {
supervisionDetailsDetails.Addrs = append(supervisionDetailsDetails.Addrs, addr)
supervisionDetailsDetails.BlockHash = append(supervisionDetailsDetails.BlockHash, stat.Details.BlockHash[i])
}
}
stat.SupervisionDetails = supervisionDetailsDetails
}
//自共识分阶段使能,综合考虑挖矿奖励和共识分配奖励,判断是否自共识使能需要采用共识的高度,而不能采用当前区块高度a.height
......@@ -511,14 +532,32 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error
}
validAddrs := getValidAddrs(nodesMap, commitAddrs)
if len(validAddrs) <= 0 {
return nil, errors.Wrapf(err, "getValidAddrs nil commitAddrs=%s", strings.Join(commitAddrs, ","))
// 获取监督节点的数据 监督节点在高度分叉后
supervisionNodesMap, supervisionNodesArry, err := a.getSupervisionNodesGroup(commit.Status.Title)
if err != nil && err != pt.ErrTitleNotExist && a.exec.GetMainHeight() >= pt.GetDappForkHeight(cfg, pt.ForkCommitTx) {
return nil, errors.Wrap(err, "getSupervisionNodesGroup")
}
return a.proCommitMsg(commit.Status, nodesMap, validAddrs)
//获取commitAddrs, bls sign 包含多个账户的聚合签名
commitSupervisionAddrs := []string{a.fromaddr}
if commit.Bls != nil {
addrs, err := a.procBlsSign(supervisionNodesArry, commit)
if err != nil {
return nil, errors.Wrap(err, "procBlsSign")
}
commitSupervisionAddrs = addrs
}
supervisionValidAddrs := getValidAddrs(supervisionNodesMap, commitSupervisionAddrs)
if len(validAddrs) <= 0 && len(supervisionValidAddrs) <= 0 {
return nil, errors.Wrapf(err, "getValidAddrs nil commitAddrs=%s commitSupervisionAddrs=%s", strings.Join(commitAddrs, ","), strings.Join(commitSupervisionAddrs, ","))
}
return a.proCommitMsg(commit.Status, nodesMap, validAddrs, supervisionNodesMap, supervisionValidAddrs)
}
func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]struct{}, commitAddrs []string) (*types.Receipt, error) {
func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]struct{}, commitAddrs []string, supervisionNodes map[string]struct{}, supervisionValidAddrs []string) (*types.Receipt, error) {
cfg := a.api.GetConfig()
err := a.preCheckCommitInfo(commit, commitAddrs)
......@@ -534,7 +573,8 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
// 在完成共识之后来的, 增加 record log, 只记录不修改已经达成的共识
if commit.Height <= titleStatus.Height {
clog.Debug("paracross.Commit record", "node", commitAddrs, "titile", commit.Title, "height", commit.Height)
return makeRecordReceipt(strings.Join(commitAddrs, ","), commit), nil
addr := strings.Join(commitAddrs, ",") + strings.Join(supervisionValidAddrs, ",")
return makeRecordReceipt(addr, commit), nil
}
// 未共识处理, 接受当前高度以及后续高度
......@@ -572,6 +612,17 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
}
}
for _, addr := range supervisionValidAddrs {
// 如有分叉, 同一个节点可能再次提交commit交易
found, index := hasCommited(stat.Details.Addrs, addr)
if found {
stat.SupervisionDetails.BlockHash[index] = commit.BlockHash
} else {
stat.SupervisionDetails.Addrs = append(stat.Details.Addrs, addr)
stat.SupervisionDetails.BlockHash = append(stat.Details.BlockHash, commit.BlockHash)
}
}
//用commit.MainBlockHeight 判断更准确,如果用a.exec.MainHeight也可以,但是可能收到MainHeight之前的高度共识tx,
// 后面loopCommitTxDone时候也是用当前共识高度大于分叉高度判断
if pt.IsParaForkHeight(cfg, commit.MainBlockHeight, pt.ForkLoopCheckCommitTxDone) {
......@@ -582,8 +633,9 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
//平行链fork pt.ForkCommitTx=0,主链在ForkCommitTx后支持nodegroup,这里平行链dappFork一定为true
if cfg.IsDappFork(commit.MainBlockHeight, pt.ParaX, pt.ForkCommitTx) {
updateCommitAddrs(stat, nodes)
updateSupervisionDetailsCommitAddrs(stat, supervisionNodes)
}
saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat)
_ = saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat)
//fork之前记录的stat 没有根据nodes更新而更新
if pt.IsParaForkHeight(cfg, stat.MainHeight, pt.ForkLoopCheckCommitTxDone) {
r := makeCommitStatReceipt(stat)
......@@ -591,7 +643,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
}
if commit.Height > titleStatus.Height+1 {
saveTitleHeight(a.db, calcTitleHeightKey(commit.Title, commit.Height), stat)
_ = saveTitleHeight(a.db, calcTitleHeightKey(commit.Title, commit.Height), stat)
//平行链由主链共识无缝切换,即接收第一个收到的高度,可以不从0开始
allow, err := a.isAllowConsensJump(commit, titleStatus)
if err != nil {
......@@ -602,7 +654,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
return receipt, nil
}
}
r, err := a.commitTxDone(commit, stat, titleStatus, nodes)
r, err := a.commitTxDone(commit, stat, titleStatus, nodes, supervisionNodes)
if err != nil {
return nil, err
}
......@@ -612,7 +664,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
//分叉以前stat里面只记录了blockhash的信息,没有crossTxHash等信息,无法通过stat直接重构出mostCommitStatus
func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.ParacrossHeightStatus, titleStatus *pt.ParacrossStatus,
nodes map[string]struct{}) (*types.Receipt, error) {
nodes map[string]struct{}, supervisionNodes map[string]struct{}) (*types.Receipt, error) {
receipt := &types.Receipt{}
clog.Debug("paracross.Commit commit", "stat.title", stat.Title, "stat.height", stat.Height, "notes", len(nodes))
......@@ -624,9 +676,23 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac
if !isCommitDone(len(nodes), mostCount) {
return receipt, nil
}
// 如果已经有监督节点
if len(supervisionNodes) > 0 {
for i, v := range stat.SupervisionDetails.Addrs {
clog.Debug("paracross.Commit commit SupervisionDetails", "addr", v, "hash", common.ToHex(stat.SupervisionDetails.BlockHash[i]))
}
mostSupervisionCount, mostSupervisionHash := GetMostCommit(stat.SupervisionDetails.BlockHash)
if !isCommitDone(len(supervisionNodes), mostSupervisionCount) {
return receipt, nil
}
clog.Debug("paracross.Commit commit SupervisionDetails ----pass", "mostSupervisionCount", mostSupervisionCount, "mostSupervisionHash", common.ToHex([]byte(mostSupervisionHash)))
}
clog.Debug("paracross.Commit commit ----pass", "most", mostCount, "mostHash", common.ToHex([]byte(mostHash)))
stat.Status = pt.ParacrossStatusCommitDone
saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat)
_ = saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat)
//之前记录的stat 状态没更新
cfg := a.api.GetConfig()
......@@ -658,7 +724,7 @@ func (a *action) commitTxDoneStep2(nodeStatus *pt.ParacrossNodeStatus, stat *pt.
titleStatus.MainHeight = nodeStatus.MainBlockHeight
titleStatus.MainHash = nodeStatus.MainBlockHash
}
saveTitle(a.db, calcTitleKey(titleStatus.Title), titleStatus)
_ = saveTitle(a.db, calcTitleKey(titleStatus.Title), titleStatus)
clog.Debug("paracross.Commit commit done", "height", nodeStatus.Height, "statusBlockHash", common.ToHex(nodeStatus.BlockHash))
......@@ -1345,29 +1411,6 @@ func (a *action) isAllowTransfer() error {
return nil
}
/*
func (a *Paracross) CrossLimits(tx *types.Transaction, index int) bool {
if tx.GroupCount < 2 {
return true
}
txs, err := a.GetTxGroup(index)
if err != nil {
clog.Error("crossLimits", "get tx group failed", err, "hash", common.ToHex(tx.Hash()))
return false
}
titles := make(map[string] struct{})
for _, txTmp := range txs {
title, err := getTitleFrom(txTmp.Execer)
if err == nil {
titles[string(title)] = struct{}{}
}
}
return len(titles) <= 1
}
*/
func (a *action) Transfer(transfer *types.AssetsTransfer, tx *types.Transaction, index int) (*types.Receipt, error) {
clog.Debug("Paracross.Exec Transfer", "symbol", transfer.Cointoken, "amount",
transfer.Amount, "to", tx.To)
......@@ -1418,3 +1461,8 @@ func (a *action) TransferToExec(transfer *types.AssetsTransferToExec, tx *types.
}
return nil, types.ErrToAddrNotSameToExecAddr
}
func getParacrossSupervisonNodes(db dbm.KV, title string) (map[string]struct{}, []string, error) {
key := calcParaSupervisionNodeGroupAddrsKey(title)
return getNodes(db, key)
}
......@@ -125,3 +125,9 @@ func (e *Paracross) Exec_ParaBindMiner(payload *pt.ParaBindMinerCmd, tx *types.T
a := newAction(e, tx)
return a.bindMiner(payload)
}
//Exec_SupervisionNodeGroupConfig exec Supervision node config
func (e *Paracross) Exec_SupervisionNodeGroupConfig(payload *pt.ParaNodeAddrConfig, tx *types.Transaction, index int) (*types.Receipt, error) {
a := newAction(e, tx)
return a.SupervisionNodeGroupConfig(payload)
}
......@@ -38,6 +38,14 @@ var (
paraBindMinderAddr string
paraBindMinderNode string
//监督节点
paraSupervisionNodes string
paraSupervisionNodeAddr string
paraSupervisionNodeGroupStatusAddrs string
paraSupervisionNodeGroupIDPrefix string
paraSupervisionSelfConsensStages string
paraSupervisionSelfConsensStageIDPrefix string
)
func setPrefix() {
......@@ -67,6 +75,12 @@ func setPrefix() {
localNodeGroupStatusTitle = "LODB-paracross-nodegroupStatusTitle-"
paraSupervisionNodes = "mavl-paracross-supervision-nodes-title-"
paraSupervisionNodeAddr = "mavl-paracross-supervision-nodes-titleAddr-"
paraSupervisionNodeGroupStatusAddrs = "mavl-paracross-supervision-nodegroup-apply-title-"
paraSupervisionNodeGroupIDPrefix = "mavl-paracross-supervision-title-nodegroupid-"
paraSupervisionSelfConsensStages = "mavl-paracross-supervision-selfconsens-stages-"
paraSupervisionSelfConsensStageIDPrefix = "mavl-paracross-supervision-selfconsens-id-"
}
func calcTitleKey(t string) []byte {
......@@ -195,3 +209,19 @@ func calcParaBindMinerAddr(node, bind string) []byte {
func calcParaBindMinerNode(node string) []byte {
return []byte(paraBindMinderNode + node)
}
func calcParaSupervisionNodeGroupAddrsKey(title string) []byte {
return []byte(fmt.Sprintf(paraSupervisionNodes+"%s", title))
}
func calcParaSupervisionNodeGroupStatusKey(title string) []byte {
return []byte(fmt.Sprintf(paraSupervisionNodeGroupStatusAddrs+"%s", title))
}
func calcParaSupervisionNodeAddrKey(title string, addr string) []byte {
return []byte(fmt.Sprintf(paraSupervisionNodeAddr+"%s-%s", title, addr))
}
func calcParaSupervisionNodeGroupIDKey(title, hash string) string {
return fmt.Sprintf(paraSupervisionNodeGroupIDPrefix+"%s-%s", title, hash)
}
......@@ -26,21 +26,14 @@ import (
"github.com/stretchr/testify/suite"
)
// 构造一个4个节点的平行链数据, 进行测试
const (
SignedType = types.SECP256K1
)
var (
MainBlockHash10 = []byte("main block hash 10")
MainBlockHeight = int64(10)
CurHeight = int64(10)
Title = string("user.p.test.")
Title = "user.p.test."
TitleHeight = int64(10)
PerBlock = []byte("block-hash-9")
CurBlock = []byte("block-hash-10")
PerState = []byte("state-hash-9")
CurState = []byte("state-hash-10")
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
......@@ -53,10 +46,8 @@ var (
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
TokenSymbol = "X"
MainBlockHeightForTransfer = int64(9)
chain33TestCfg = types.NewChain33Config(testnode.DefaultConfig)
chain33TestMainCfg = types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"test\"", 1))
chain33TestCfg = types.NewChain33Config(testnode.DefaultConfig)
chain33TestMainCfg = types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"test\"", 1))
)
type CommitTestSuite struct {
......@@ -92,7 +83,6 @@ func init() {
}
func (suite *CommitTestSuite) SetupSuite() {
suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024)
// memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb
//suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024)
......@@ -118,7 +108,7 @@ func (suite *CommitTestSuite) SetupSuite() {
// setup title nodes : len = 4
nodeConfigKey := calcManageConfigNodesKey(Title)
nodeValue := makeNodeInfo(Title, Title, 4)
suite.stateDB.Set(nodeConfigKey, types.Encode(nodeValue))
_ = suite.stateDB.Set(nodeConfigKey, types.Encode(nodeValue))
value, err := suite.stateDB.Get(nodeConfigKey)
if err != nil {
suite.T().Error("get setup title failed", err)
......@@ -129,7 +119,7 @@ func (suite *CommitTestSuite) SetupSuite() {
stageKey := calcParaSelfConsStagesKey()
stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes}
stages := &pt.SelfConsensStages{Items: []*pt.SelfConsensStage{stage}}
suite.stateDB.Set(stageKey, types.Encode(stages))
_ = suite.stateDB.Set(stageKey, types.Encode(stages))
value, err = suite.stateDB.Get(stageKey)
if err != nil {
suite.T().Error("get setup stages failed", err)
......@@ -142,7 +132,7 @@ func (suite *CommitTestSuite) SetupSuite() {
titleStatus.Title = Title
titleStatus.Height = CurHeight - 1
titleStatus.BlockHash = PerBlock
saveTitle(suite.stateDB, calcTitleKey(Title), &titleStatus)
_ = saveTitle(suite.stateDB, calcTitleKey(Title), &titleStatus)
// setup api
hashes := &types.ReqHashes{Hashes: [][]byte{MainBlockHash10}}
......@@ -200,13 +190,13 @@ func signTx(s suite.Suite, tx *types.Transaction, hexPrivKey string) (*types.Tra
return tx, err
}
bytes, err := common.FromHex(hexPrivKey[:])
bytesData, err := common.FromHex(hexPrivKey[:])
if err != nil {
s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err)
return tx, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
privKey, err := c.PrivKeyFromBytes(bytesData)
if err != nil {
s.T().Error("TestExec", "PrivKeyFromBytes failed", err)
return tx, err
......@@ -224,13 +214,13 @@ func getPrivKey(s suite.Suite, hexPrivKey string) (crypto.PrivKey, error) {
return nil, err
}
bytes, err := common.FromHex(hexPrivKey[:])
bytesData, err := common.FromHex(hexPrivKey[:])
if err != nil {
s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err)
return nil, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
privKey, err := c.PrivKeyFromBytes(bytesData)
if err != nil {
s.T().Error("TestExec", "PrivKeyFromBytes failed", err)
return nil, err
......@@ -311,6 +301,7 @@ func checkDoneReceipt(suite suite.Suite, receipt *types.Receipt, commitCnt int)
}
func checkRecordReceipt(suite *CommitTestSuite, receipt *types.Receipt, commitCnt int) {
_ = commitCnt
assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
assert.Len(suite.T(), receipt.KV, 0)
assert.Len(suite.T(), receipt.Logs, 1)
......@@ -363,105 +354,6 @@ func TestGetTitle(t *testing.T) {
assert.Equal(t, titleExpect, title)
}
/*
func TestCrossLimits(t *testing.T) {
stateDB, _ := dbm.NewGoMemDB("state", "state", 1024)
localDB := new(dbmock.KVDB)
api := new(apimock.QueueProtocolAPI)
exec := newParacross().(*Paracross)
exec.SetLocalDB(localDB)
exec.SetStateDB(stateDB)
exec.SetEnv(0, 0, 0)
exec.SetAPI(api)
func (s *VoteTestSuite) TestFilterTxsForPara() {
tx1, err := createAssetTransferTx(s.Suite, PrivKeyA, nil)
s.Nil(err)
tx2, err := createParaNormalTx(s.Suite, PrivKeyB, nil)
s.Nil(err)
tx3, err := createParaNormalTx(s.Suite,PrivKeyA,[]byte("toA"))
s.Nil(err)
tx4, err := createCrossParaTx(s.Suite, []byte("toB"))
s.Nil(err)
tx5, err := createParaNormalTx(s.Suite,PrivKeyA,[]byte("toB"))
s.Nil(err)
tx345 := []*types.Transaction{tx3, tx4,tx5}
txGroup345, err := createTxsGroup(s.Suite, tx345)
s.Nil(err)
tx6, err := createCrossParaTx(s.Suite, nil)
s.Nil(err)
tx7, err := createCrossParaTx(s.Suite, nil)
s.Nil(err)
tx67 := []*types.Transaction{tx6, tx7}
txGroup67, err := createTxsGroup(s.Suite, tx67)
s.Nil(err)
tx71, err := createParaNormalTx(s.Suite,PrivKeyA,[]byte("toA"))
s.Nil(err)
tx72, err := createCrossParaTx(s.Suite, []byte("toB"))
s.Nil(err)
tx73, err := createParaNormalTx(s.Suite,PrivKeyA,[]byte("toB"))
s.Nil(err)
tx777 := []*types.Transaction{tx71, tx72,tx73}
txGroup777, err := createTxsGroup(s.Suite, tx777)
s.Nil(err)
tx8, err := createAssetTransferTx(s.Suite, PrivKeyA, nil)
s.Nil(err)
tx9, err := createAssetTransferTx(s.Suite, PrivKeyC, nil)
s.Nil(err)
txs := []*types.Transaction{tx1, tx2}
txs = append(txs, txGroup345...)
txs = append(txs, txGroup67...)
txs = append(txs, txGroup777...)
txs = append(txs, tx8)
txs = append(txs, tx9)
errlog := &types.ReceiptLog{Ty: types.TyLogErr, Log: []byte("")}
feelog := &types.Receipt{}
feelog.Logs = append(feelog.Logs, errlog)
recpt1 := &types.ReceiptData{Ty: types.ExecPack,Logs:feelog.Logs}
recpt2 := &types.ReceiptData{Ty: types.ExecPack}
recpt3 := &types.ReceiptData{Ty: types.ExecOk}
recpt4 := &types.ReceiptData{Ty: types.ExecOk}
recpt5 := &types.ReceiptData{Ty: types.ExecOk}
recpt6 := &types.ReceiptData{Ty: types.ExecPack,Logs:feelog.Logs}
recpt7 := &types.ReceiptData{Ty: types.ExecPack}
recpt71 := &types.ReceiptData{Ty: types.ExecPack}
recpt72 := &types.ReceiptData{Ty: types.ExecPack}
recpt73 := &types.ReceiptData{Ty: types.ExecPack}
recpt8 := &types.ReceiptData{Ty: types.ExecPack,Logs:feelog.Logs}
recpt9 := &types.ReceiptData{Ty: types.ExecOk}
receipts := []*types.ReceiptData{recpt1, recpt2, recpt3, recpt4, recpt5, recpt6, recpt7, recpt71,recpt72, recpt73, recpt8,recpt9}
block := &types.Block{Txs: txs}
detail := &types.BlockDetail{
Block: block,
Receipts: receipts,
}
rst := FilterTxsForPara(Title, detail)
filterTxs := []*types.Transaction{ tx2,tx3, tx4, tx5,tx71,tx72,tx73,tx9}
s.Equal( filterTxs, rst)
}
tx := &types.Transaction{Execer: []byte("p.user.test.paracross")}
res := exec.CrossLimits(tx, 1)
assert.True(t, res)
}
*/
type VoteTestSuite struct {
suite.Suite
stateDB dbm.KV
......@@ -489,7 +381,7 @@ func (s *VoteTestSuite) SetupSuite() {
stageKey := calcParaSelfConsStagesKey()
stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes}
stages := &pt.SelfConsensStages{Items: []*pt.SelfConsensStage{stage}}
s.stateDB.Set(stageKey, types.Encode(stages))
_ = s.stateDB.Set(stageKey, types.Encode(stages))
value, err := s.stateDB.Get(stageKey)
if err != nil {
s.T().Error("get setup stages failed", err)
......@@ -565,7 +457,7 @@ func (s *VoteTestSuite) TestVoteTx() {
//s.T().Log(string(kv.GetKey()))
if bytes.Equal(key, kv.Key) {
var rst pt.ParacrossNodeStatus
types.Decode(kv.GetValue(), &rst)
_ = types.Decode(kv.GetValue(), &rst)
s.Equal([]byte{0x4d}, rst.TxResult)
s.Equal([]byte{0x25}, rst.CrossTxResult)
s.Equal(7, len(rst.TxHashs))
......@@ -662,7 +554,7 @@ func (s *VoteTestSuite) TestVoteTxFork() {
//s.T().Log(string(kv.GetKey()))
if bytes.Equal(key, kv.Key) {
var rst pt.ParacrossNodeStatus
types.Decode(kv.GetValue(), &rst)
_ = types.Decode(kv.GetValue(), &rst)
s.Equal([]byte("8e"), rst.TxResult)
s.Equal([]byte("22"), rst.CrossTxResult)
s.Equal(1, len(rst.TxHashs))
......@@ -739,7 +631,7 @@ func createTxsGroup(s suite.Suite, txs []*types.Transaction) ([]*types.Transacti
}
privKey, _ := getPrivKey(s, PrivKeyA)
for i := range group.Txs {
group.SignN(i, int32(types.SECP256K1), privKey)
_ = group.SignN(i, int32(types.SECP256K1), privKey)
}
return group.Txs, nil
}
......@@ -794,11 +686,11 @@ func TestUpdateCommitBlockHashs(t *testing.T) {
}
updateCommitBlockHashs(stat, commit)
assert.Equal(t, int(1), len(stat.BlockDetails.BlockHashs))
assert.Equal(t, 1, len(stat.BlockDetails.BlockHashs))
assert.Equal(t, commit.BlockHash, stat.BlockDetails.BlockHashs[0])
updateCommitBlockHashs(stat, commit)
assert.Equal(t, int(1), len(stat.BlockDetails.BlockHashs))
assert.Equal(t, 1, len(stat.BlockDetails.BlockHashs))
assert.Equal(t, commit.BlockHash, stat.BlockDetails.BlockHashs[0])
commit2 := &pt.ParacrossNodeStatus{
......@@ -812,7 +704,7 @@ func TestUpdateCommitBlockHashs(t *testing.T) {
CrossTxHashs: [][]byte{[]byte("hash2")},
}
updateCommitBlockHashs(stat, commit2)
assert.Equal(t, int(2), len(stat.BlockDetails.BlockHashs))
assert.Equal(t, 2, len(stat.BlockDetails.BlockHashs))
assert.Equal(t, commit2.BlockHash, stat.BlockDetails.BlockHashs[1])
}
......
......@@ -280,7 +280,6 @@ func (a *action) nodeJoin(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil
}
func (a *action) nodeQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
......@@ -309,7 +308,6 @@ func (a *action) nodeQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
Votes: &pt.ParaNodeVoteDetail{},
Height: a.height}
return makeNodeConfigReceipt(a.fromaddr, config, nil, stat), nil
}
func (a *action) nodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
......@@ -357,7 +355,6 @@ func (a *action) nodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, erro
}
return nil, errors.Wrapf(pt.ErrParaUnSupportNodeOper, "nodeid %s was quit status:%d", config.Id, stat.Status)
}
func (a *action) nodeModify(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
......@@ -602,7 +599,6 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
stat.Status = pt.ParaApplyClosed
stat.Height = a.height
}
} else {
if stat.Status == pt.ParaApplyJoining {
r, err := unpdateNodeGroup(a.db, config.Title, stat.TargetAddr, true)
......@@ -651,7 +647,6 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
receiptDone := makeVoteDoneReceipt(stat, len(nodes), len(stat.Votes.Addrs), most, pt.ParaNodeVoteStr[vote], stat.Status)
receipt = mergeReceipt(receipt, receiptDone)
return receipt, nil
}
func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) {
......@@ -929,7 +924,6 @@ func (a *action) nodeGroupApproveModify(config *pt.ParaNodeGroupConfig, modify *
receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil
}
func (a *action) nodeGroupApproveApply(config *pt.ParaNodeGroupConfig, apply *pt.ParaNodeGroupStatus) (*types.Receipt, error) {
......@@ -971,7 +965,6 @@ func (a *action) nodeGroupApproveApply(config *pt.ParaNodeGroupConfig, apply *pt
}
return receipt, nil
}
// NodeGroupApprove super addr approve the node group apply
......@@ -1068,13 +1061,11 @@ func (a *action) NodeGroupConfig(config *pt.ParaNodeGroupConfig) (*types.Receipt
return nil, err
}
return a.nodeGroupApply(config)
} else if config.Op == pt.ParacrossNodeGroupApprove {
if config.Id == "" {
return nil, types.ErrInvalidParam
}
return a.nodeGroupApprove(config)
} else if config.Op == pt.ParacrossNodeGroupQuit {
if config.Id == "" {
return nil, types.ErrInvalidParam
......@@ -1122,5 +1113,4 @@ func (a *action) NodeConfig(config *pt.ParaNodeAddrConfig) (*types.Receipt, erro
default:
return nil, pt.ErrParaUnSupportNodeOper
}
}
......@@ -25,7 +25,7 @@ import (
var (
PrivKey14K = "CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944" // 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
Account14K = "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
Account1MC = "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
//Account1MC = "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
applyAddrs = "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4, 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR, 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k,1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
Account12Q = "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
......@@ -60,7 +60,6 @@ func createRawNodeGroupApplyTx(apply *pt.ParaNodeGroupConfig) (*types.Transactio
}
return tx, nil
}
type NodeManageTestSuite struct {
......@@ -68,13 +67,10 @@ type NodeManageTestSuite struct {
stateDB dbm.KV
localDB *dbmock.KVDB
api *apimock.QueueProtocolAPI
exec *Paracross
//title string
exec *Paracross
}
func (suite *NodeManageTestSuite) SetupSuite() {
suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024)
// memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb
//suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024)
......@@ -90,12 +86,6 @@ func (suite *NodeManageTestSuite) SetupSuite() {
suite.exec.SetBlockInfo([]byte(""), []byte(""), 3)
enableParacrossTransfer = false
//forkHeight := types.GetDappFork(pt.ParaX, pt.ForkCommitTx)
//if forkHeight == types.MaxHeight {
// types.ReplaceDappFork(MainTitle, pt.ParaX, pt.ForkCommitTx, 0)
//
//}
chain33TestCfg.S("config.consensus.sub.para.MainForkParacrossCommitTx", int64(1))
chain33TestCfg.S("config.exec.sub.manage.superManager", []interface{}{Account12Q})
......@@ -105,7 +95,6 @@ func (suite *NodeManageTestSuite) SetupSuite() {
Block: &types.Block{},
}
MainBlockHash10 = blockDetail.Block.Hash(chain33TestCfg)
}
func (suite *NodeManageTestSuite) TestSetup() {
......@@ -145,7 +134,6 @@ func checkGroupApplyReceipt(suite *NodeManageTestSuite, receipt *types.Receipt)
assert.Len(suite.T(), receipt.Logs, 1)
assert.Equal(suite.T(), int32(pt.TyLogParaNodeGroupConfig), receipt.Logs[0].Ty)
}
func checkGroupApproveReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
......@@ -164,7 +152,6 @@ func checkJoinReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
assert.Equal(suite.T(), int32(pt.TyLogParaNodeConfig), receipt.Logs[0].Ty)
assert.Equal(suite.T(), int32(pt.ParaApplyJoining), stat.Status)
assert.NotNil(suite.T(), stat.Votes)
}
func checkQuitReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
......@@ -179,7 +166,6 @@ func checkQuitReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
assert.Equal(suite.T(), int32(pt.TyLogParaNodeConfig), receipt.Logs[0].Ty)
assert.Equal(suite.T(), int32(pt.ParaApplyQuiting), stat.Status)
assert.NotNil(suite.T(), stat.Votes)
}
func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int) {
......@@ -189,15 +175,14 @@ func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count
err := types.Decode(receipt.KV[0].Value, &stat)
assert.Nil(suite.T(), err, "decode ParaNodeAddrStatus failed")
assert.Len(suite.T(), stat.Votes.Votes, count)
}
func checkVoteDoneReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int, join bool) {
_ = count
suite.NotNil(receipt)
assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
suite.T().Log("checkVoteDoneReceipt", "kvlen", len(receipt.KV))
_, arry, err := getParacrossNodes(suite.stateDB, Title)
suite.Suite.Nil(err)
if join {
......@@ -264,7 +249,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfigQuit() {
nodeCommit(suite, PrivKeyB, tx)
//checkGroupApproveReceipt(suite, receipt)
}
func (suite *NodeManageTestSuite) testNodeGroupConfig() {
......@@ -296,7 +280,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfig() {
receipt = nodeCommit(suite, PrivKey12Q, tx)
checkGroupApproveReceipt(suite, receipt)
}
func (suite *NodeManageTestSuite) testNodeConfig() {
......@@ -342,7 +325,6 @@ func (suite *NodeManageTestSuite) testNodeConfig() {
func (suite *NodeManageTestSuite) TestExec() {
suite.testNodeGroupConfig()
suite.testNodeConfig()
}
func TestNodeManageSuite(t *testing.T) {
......@@ -350,7 +332,6 @@ func TestNodeManageSuite(t *testing.T) {
}
func (suite *NodeManageTestSuite) TearDownSuite() {
}
func TestGetAddrGroup(t *testing.T) {
......@@ -374,7 +355,6 @@ func TestGetAddrGroup(t *testing.T) {
ret = getConfigAddrs(addrs)
assert.Equal(t, []string(nil), ret)
assert.Equal(t, 0, len(ret))
}
func TestUpdateVotes(t *testing.T) {
......@@ -402,5 +382,4 @@ func TestGetNodeIdSuffix(t *testing.T) {
id = "mavl-paracross-title-nodegroupid-user.p.para.-0xb6cd0274aa5f839fa2291ecfbfc626b494aacac7587a61e444e9f848a4c02d7b-1"
rtID = getParaNodeIDSuffix(id)
assert.Equal(t, txID, rtID)
}
package executor
import (
"fmt"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/pkg/errors"
"strconv"
"strings"
)
func (a *action) checkValidSupervisionNode(config *pt.ParaNodeAddrConfig) (bool, error) {
nodes, _, err := getParacrossSupervisonNodes(a.db, config.Title)
if err != nil && !isNotFound(err) {
return false, errors.Wrapf(err, "getNodes for title:%s", config.Title)
}
//有可能申请地址和配置地址不是同一个
if validNode(config.Addr, nodes) {
return true, nil
}
return false, nil
}
func getSupervisionNodeGroupStatus(db dbm.KV, title string) (*pt.ParaNodeGroupStatus, error) {
key := calcParaSupervisionNodeGroupStatusKey(title)
val, err := db.Get(key)
if err != nil {
return nil, err
}
var status pt.ParaNodeGroupStatus
err = types.Decode(val, &status)
return &status, err
}
func getSupervisionNodeAddr(db dbm.KV, title, addr string) (*pt.ParaNodeAddrIdStatus, error) {
key := calcParaSupervisionNodeAddrKey(title, addr)
val, err := db.Get(key)
if err != nil {
return nil, err
}
var status pt.ParaNodeAddrIdStatus
err = types.Decode(val, &status)
return &status, err
}
//func (a *action) checkSupervisionNodeGroupExist(title string) error {
// key := calcParaSupervisionNodeGroupAddrsKey(title)
// value, err := a.db.Get(key)
// if err != nil && !isNotFound(err) {
// return err
// }
// if value != nil {
// clog.Error("node group apply, group existed")
// return pt.ErrParaSupervisionNodeGroupExisted
// }
//
// return nil
//}
func makeSupervisionNodeGroupIDReceipt(addr string, prev, current *pt.ParaNodeGroupStatus) *types.Receipt {
log := &pt.ReceiptParaNodeGroupConfig{
Addr: addr,
Prev: prev,
Current: current,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: []byte(current.Id), Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaSupervisionNodeGroupConfig,
Log: types.Encode(log),
},
},
}
}
func (a *action) supervisionNodeGroupApply(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
// 是否已经申请
addrExist, err := a.checkValidSupervisionNode(config)
if err != nil {
return nil, err
}
if addrExist {
return nil, errors.Wrapf(pt.ErrParaSupervisionNodeAddrExisted, "nodeAddr existed:%s", config.Addr)
}
// 判断和监督组冻结金额是否一致
nodeGroupStatus, err := getSupervisionNodeGroupStatus(a.db, config.Title)
if err != nil && !isNotFound(err) {
return nil, errors.Wrapf(pt.ErrParaSupervisionNodeGroupNotSet, "nodegroup not exist:%s", config.Title)
}
if nodeGroupStatus != nil && config.CoinsFrozen < nodeGroupStatus.CoinsFrozen {
return nil, errors.Wrapf(pt.ErrParaNodeGroupFrozenCoinsNotEnough,
"coinFrozen not enough:%d,expected:%d", config.CoinsFrozen, nodeGroupStatus.CoinsFrozen)
}
// 在主链上冻结金额
receipt := &types.Receipt{Ty: types.ExecOk}
cfg := a.api.GetConfig()
if !cfg.IsPara() {
r, err := a.nodeGroupCoinsFrozen(a.fromaddr, config.CoinsFrozen, 1)
if err != nil {
return nil, err
}
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
}
// 判断申请节点之前没有申请或者状态不是申请退出
addrStat, err := getSupervisionNodeAddr(a.db, config.Title, config.Addr)
if err != nil && !isNotFound(err) {
return nil, errors.Wrapf(err, "nodeJoin get title=%s,nodeAddr=%s", config.Title, config.Addr)
}
if addrStat != nil && addrStat.Status != pt.ParaApplyQuited {
return nil, errors.Wrapf(pt.ErrParaNodeAddrExisted, "nodeJoin nodeAddr existed:%s,status:%d", config.Addr, addrStat.Status)
}
targetAddrs := ""
blsPubKeys := ""
if nodeGroupStatus != nil {
targetAddrs = nodeGroupStatus.TargetAddrs + ","
blsPubKeys = nodeGroupStatus.BlsPubKeys + ","
}
targetAddrs += config.Addr
blsPubKeys += config.BlsPubKey
stat := &pt.ParaNodeGroupStatus{
Id: calcParaSupervisionNodeGroupIDKey(config.Title, common.ToHex(a.txhash)),
Status: pt.ParacrossSupervisionNodeApply,
Title: config.Title,
TargetAddrs: targetAddrs,
BlsPubKeys: blsPubKeys,
CoinsFrozen: config.CoinsFrozen,
FromAddr: a.fromaddr,
Height: a.height,
}
r := makeSupervisionNodeGroupIDReceipt(a.fromaddr, nil, stat)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil
}
func (a *action) checkSupervisionNodeGroupExist(title string) error {
key := calcParaSupervisionNodeGroupAddrsKey(title)
value, err := a.db.Get(key)
if err != nil && !isNotFound(err) {
return err
}
if value != nil {
clog.Error("node group apply, group existed")
return pt.ErrParaNodeGroupExisted
}
return nil
}
func makeParaSupervisionNodeGroupReceipt(title string, prev, current *types.ConfigItem) *types.Receipt {
key := calcParaSupervisionNodeGroupAddrsKey(title)
log := &types.ReceiptConfig{Prev: prev, Current: current}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaSupervisionNodeGroupAddrsUpdate,
Log: types.Encode(log),
},
},
}
}
func makeSupervisionNodeConfigReceipt(fromAddr string, config *pt.ParaNodeAddrConfig, prev, current *pt.ParaNodeIdStatus) *types.Receipt {
log := &pt.ReceiptParaNodeConfig{
Addr: fromAddr,
Config: config,
Prev: prev,
Current: current,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: []byte(current.Id), Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaSupervisionNodeConfig,
Log: types.Encode(log),
},
},
}
}
func (a *action) supervisionNodeGroupCreate(status *pt.ParaNodeGroupStatus) (*types.Receipt, error) {
nodes := strings.Split(status.TargetAddrs, ",")
var item types.ConfigItem
key := calcParaSupervisionNodeGroupAddrsKey(status.Title)
item.Key = string(key)
emptyValue := &types.ArrayConfig{Value: make([]string, 0)}
arr := types.ConfigItem_Arr{Arr: emptyValue}
item.Value = &arr
item.GetArr().Value = append(item.GetArr().Value, nodes...)
item.Addr = a.fromaddr
receipt := makeParaSupervisionNodeGroupReceipt(status.Title, nil, &item)
var blsPubKeys []string
if len(status.BlsPubKeys) > 0 {
blsPubKeys = strings.Split(status.BlsPubKeys, ",")
}
//update addr status
for i, addr := range nodes {
stat := &pt.ParaNodeIdStatus{
Id: status.Id + "-" + strconv.Itoa(i),
Status: pt.ParaApplyClosed,
Title: status.Title,
TargetAddr: addr,
Votes: &pt.ParaNodeVoteDetail{Addrs: []string{a.fromaddr}, Votes: []string{"yes"}},
CoinsFrozen: status.CoinsFrozen,
FromAddr: status.FromAddr,
Height: a.height}
if len(blsPubKeys) > 0 {
stat.BlsPubKey = blsPubKeys[i]
}
r := makeSupervisionNodeConfigReceipt(a.fromaddr, nil, nil, stat)
receipt = mergeReceipt(receipt, r)
r, err := a.updateSupervisionNodeAddrStatus(stat)
if err != nil {
return nil, err
}
receipt = mergeReceipt(receipt, r)
}
return receipt, nil
}
func makeParaSupervisionNodeStatusReceipt(fromAddr string, prev, current *pt.ParaNodeAddrIdStatus) *types.Receipt {
key := calcParaSupervisionNodeAddrKey(current.Title, current.Addr)
log := &pt.ReceiptParaNodeAddrStatUpdate{
FromAddr: fromAddr,
Prev: prev,
Current: current,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaSupervisionNodeStatusUpdate,
Log: types.Encode(log),
},
},
}
}
//由于propasal id 和quit id分开,quit id不知道对应addr proposal id的coinfrozen信息,需要维护一个围绕addr的数据库结构信息
func (a *action) updateSupervisionNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt, error) {
cfg := a.api.GetConfig()
addrStat, err := getSupervisionNodeAddr(a.db, stat.Title, stat.TargetAddr)
if err != nil {
if !isNotFound(err) {
return nil, errors.Wrapf(err, "nodeAddr:%s get error", stat.TargetAddr)
}
addrStat = &pt.ParaNodeAddrIdStatus{}
addrStat.Title = stat.Title
addrStat.Addr = stat.TargetAddr
addrStat.BlsPubKey = stat.BlsPubKey
addrStat.Status = pt.ParaApplyJoined
addrStat.ProposalId = stat.Id
addrStat.QuitId = ""
return makeParaSupervisionNodeStatusReceipt(a.fromaddr, nil, addrStat), nil
}
preStat := *addrStat
if stat.Status == pt.ParaApplyJoining {
addrStat.Status = pt.ParaApplyJoined
addrStat.ProposalId = stat.Id
addrStat.QuitId = ""
return makeParaSupervisionNodeStatusReceipt(a.fromaddr, &preStat, addrStat), nil
}
if stat.Status == pt.ParaApplyQuiting {
proposalStat, err := getNodeID(a.db, addrStat.ProposalId)
if err != nil {
return nil, errors.Wrapf(err, "nodeAddr:%s quiting wrong proposeid:%s", stat.TargetAddr, addrStat.ProposalId)
}
addrStat.Status = pt.ParaApplyQuited
addrStat.QuitId = stat.Id
receipt := makeParaSupervisionNodeStatusReceipt(a.fromaddr, &preStat, addrStat)
if !cfg.IsPara() {
r, err := a.nodeGroupCoinsActive(proposalStat.FromAddr, proposalStat.CoinsFrozen, 1)
if err != nil {
return nil, err
}
receipt = mergeReceipt(receipt, r)
}
return receipt, nil
}
return nil, errors.Wrapf(pt.ErrParaNodeOpStatusWrong, "nodeAddr:%s get wrong status:%d", stat.TargetAddr, stat.Status)
}
func (a *action) supervisionNodeGroupApproveApply(config *pt.ParaNodeAddrConfig, apply *pt.ParaNodeGroupStatus) (*types.Receipt, error) {
err := a.checkSupervisionNodeGroupExist(config.Title)
if err != nil {
return nil, err
}
if apply.CoinsFrozen < config.CoinsFrozen {
return nil, errors.Wrapf(pt.ErrParaNodeGroupFrozenCoinsNotEnough, "id not enough coins apply:%d,config:%d", apply.CoinsFrozen, config.CoinsFrozen)
}
receipt := &types.Receipt{Ty: types.ExecOk}
//create the node group
r, err := a.supervisionNodeGroupCreate(apply)
if err != nil {
return nil, errors.Wrapf(err, "nodegroup create:title:%s,addrs:%s", config.Title, apply.TargetAddrs)
}
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
copyStat := *apply
apply.Status = pt.ParacrossNodeGroupApprove
apply.Height = a.height
r = makeNodeGroupIDReceipt(a.fromaddr, &copyStat, apply)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
r = makeParaNodeGroupStatusReceipt(config.Title, a.fromaddr, nil, apply)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
cfg := a.api.GetConfig()
if cfg.IsPara() && cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaSelfConsStages) {
//不允许主链成功平行链失败导致不一致的情况,这里如果失败则手工设置init stage ???
r = supervisionSelfConsentInitStage(cfg)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
}
return receipt, nil
}
func supervisionSelfConsentInitStage(cfg *types.Chain33Config) *types.Receipt {
isEnable := cfg.IsEnable(pt.ParaConsSubConf + "." + pt.ParaSelfConsInitConf)
stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes}
if isEnable {
stage.Enable = pt.ParaConfigNo
}
stages := &pt.SelfConsensStages{Items: []*pt.SelfConsensStage{stage}}
return makeStageSupervisionGroupReceipt(nil, stages)
}
func makeStageSupervisionGroupReceipt(prev, current *pt.SelfConsensStages) *types.Receipt {
key := []byte(fmt.Sprintf(paraSupervisionSelfConsensStages))
log := &pt.ReceiptSelfConsStagesUpdate{Prev: prev, Current: current}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaStageSupervisionGroupUpdate,
Log: types.Encode(log),
},
},
}
}
func getSupervisionNodeGroupID(cfg *types.Chain33Config, db dbm.KV, title string, height int64, id string) (*pt.ParaNodeGroupStatus, error) {
if pt.IsParaForkHeight(cfg, height, pt.ForkLoopCheckCommitTxDone) {
id = calcParaSupervisionNodeGroupIDKey(title, id)
}
val, err := getDb(db, []byte(id))
if err != nil {
return nil, err
}
var status pt.ParaNodeGroupStatus
err = types.Decode(val, &status)
return &status, err
}
// NodeGroupApprove super addr approve the node group apply
func (a *action) supervisionNodeGroupApprove(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
cfg := a.api.GetConfig()
//只在主链检查
if !cfg.IsPara() && !isSuperManager(cfg, a.fromaddr) {
return nil, errors.Wrapf(types.ErrNotAllow, "node group approve not super manager:%s", a.fromaddr)
}
id, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id)
if err != nil {
return nil, err
}
if config.Title != id.Title {
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", config.Title, id.Title)
}
return a.supervisionNodeGroupApproveApply(config, id)
}
func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
cfg := a.api.GetConfig()
status, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id)
if err != nil {
return nil, err
}
//只能提案发起人撤销
if a.fromaddr != status.FromAddr {
return nil, errors.Wrapf(types.ErrNotAllow, "id create by:%s,not by:%s", status.FromAddr, a.fromaddr)
}
if config.Title != status.Title {
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", config.Title, status.Title)
}
//approved or quited
if status.Status != pt.ParacrossNodeGroupApply {
return nil, errors.Wrapf(pt.ErrParaNodeOpStatusWrong, "node group apply not apply:%d", status.Status)
}
applyAddrs := strings.Split(status.TargetAddrs, ",")
receipt := &types.Receipt{Ty: types.ExecOk}
//main chain
if !cfg.IsPara() {
r, err := a.nodeGroupCoinsActive(status.FromAddr, status.CoinsFrozen, int64(len(applyAddrs)))
if err != nil {
return nil, err
}
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
}
copyStat := *status
status.Status = pt.ParacrossNodeGroupQuit
status.Height = a.height
r := makeSupervisionNodeGroupIDReceipt(a.fromaddr, &copyStat, status)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil
}
func (a *action) SupervisionNodeGroupConfig(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
cfg := a.api.GetConfig()
if !validTitle(cfg, config.Title) {
return nil, pt.ErrInvalidTitle
}
if !types.IsParaExecName(string(a.tx.Execer)) && cfg.IsDappFork(a.exec.GetMainHeight(), pt.ParaX, pt.ForkParaAssetTransferRbk) {
return nil, errors.Wrapf(types.ErrInvalidParam, "exec=%s,should prefix with user.p.", string(a.tx.Execer))
}
if config.Op == pt.ParacrossSupervisionNodeApply {
return a.supervisionNodeGroupApply(config)
} else if config.Op == pt.ParacrossSupervisionNodeApprove {
if config.Id == "" {
return nil, types.ErrInvalidParam
}
return a.supervisionNodeGroupApprove(config)
} else if config.Op == pt.ParacrossSupervisionNodeQuit {
if config.Id == "" {
return nil, types.ErrInvalidParam
}
return a.supervisionNodeGroupQuit(config)
}
return nil, pt.ErrParaUnSupportNodeOper
}
平行链目前有超级节点和普通节点两个角色,对超级节点账户组没有制衡的角色
波卡有钓鱼节点的概念,可以监督验证节点。chain33 也可以增加一个监督节点
基本功能如下:
1. 监督节点可以是一个节点也可以是监督节点账户组。
1. 监督节点账户组和超级节点账户组共识逻辑一样,都是超过2/3节点的共识结果,否则认为没有达成共识
1. 如果监督节点或超级节点账户组任何一方没有达成共识,认为暂时没有达成共识
1. 如果监督节点和超级节点的共识结果不一致,则暂时搁置共识,等待修正共识。不做处罚处理
1. 所有参与共识的监督节点和超级节点 前2/3节点平分挖矿奖励。
\ No newline at end of file
......@@ -20,13 +20,14 @@ message ParacrossStatusBlockDetails {
message ParacrossHeightStatus {
// ing, done
int32 status = 1;
string title = 2;
int64 height = 3;
ParacrossStatusDetails details = 4;
int64 mainHeight = 5;
bytes mainHash = 6;
ParacrossStatusBlockDetails blockDetails = 7;
int32 status = 1;
string title = 2;
int64 height = 3;
ParacrossStatusDetails details = 4;
ParacrossStatusDetails supervisionDetails = 5;
int64 mainHeight = 6;
bytes mainHash = 7;
ParacrossStatusBlockDetails blockDetails = 8;
}
message ParacrossHeightStatusRsp {
......
......@@ -59,4 +59,10 @@ var (
ErrConsensClosed = errors.New("ErrConsensClosed")
//ErrBlsSignVerify bls12-381 aggregate sign verify
ErrBlsSignVerify = errors.New("ErrBlsSignVerify")
//ErrParaSupervisionNodeAddrExisted node addr exist in group
ErrParaSupervisionNodeAddrExisted = errors.New("ErrParaSupervisionNodeAddrExisted")
//ErrParaSupervisionNodeGroupNotSet para config node group not set by take over
ErrParaSupervisionNodeGroupNotSet = errors.New("ErrParaSupervisionNodeGroupNotSet")
//ErrParaSupervisionNodeGroupExisted para config group taked over alreay
ErrParaSupervisionNodeGroupExisted = errors.New("ErrParaSupervisionNodesExisted")
)
......@@ -49,6 +49,12 @@ const (
TyLogParaCrossAssetTransfer = 670
TyLogParaBindMinerAddr = 671
TyLogParaBindMinerNode = 672
TyLogParaSupervisionNodeGroupConfig = 680
TyLogParaSupervisionNodeGroupAddrsUpdate
TyLogParaSupervisionNodeConfig
TyLogParaSupervisionNodeStatusUpdate
TyLogParaStageSupervisionGroupUpdate
)
// action type
......@@ -164,6 +170,12 @@ const (
ParacrossNodeGroupModify
)
const (
ParacrossSupervisionNodeApply = iota + 1
ParacrossSupervisionNodeApprove
ParacrossSupervisionNodeQuit
)
var (
// ParacrossActionCommitStr Commit string
ParacrossActionCommitStr = string("Commit")
......
......@@ -6,13 +6,12 @@ package types
import (
context "context"
fmt "fmt"
math "math"
types "github.com/33cn/chain33/types"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
......@@ -128,9 +127,10 @@ type ParacrossHeightStatus struct {
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"`
Details *ParacrossStatusDetails `protobuf:"bytes,4,opt,name=details,proto3" json:"details,omitempty"`
MainHeight int64 `protobuf:"varint,5,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"`
MainHash []byte `protobuf:"bytes,6,opt,name=mainHash,proto3" json:"mainHash,omitempty"`
BlockDetails *ParacrossStatusBlockDetails `protobuf:"bytes,7,opt,name=blockDetails,proto3" json:"blockDetails,omitempty"`
SupervisionDetails *ParacrossStatusDetails `protobuf:"bytes,5,opt,name=supervisionDetails,proto3" json:"supervisionDetails,omitempty"`
MainHeight int64 `protobuf:"varint,6,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"`
MainHash []byte `protobuf:"bytes,7,opt,name=mainHash,proto3" json:"mainHash,omitempty"`
BlockDetails *ParacrossStatusBlockDetails `protobuf:"bytes,8,opt,name=blockDetails,proto3" json:"blockDetails,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -189,6 +189,13 @@ func (m *ParacrossHeightStatus) GetDetails() *ParacrossStatusDetails {
return nil
}
func (m *ParacrossHeightStatus) GetSupervisionDetails() *ParacrossStatusDetails {
if m != nil {
return m.SupervisionDetails
}
return nil
}
func (m *ParacrossHeightStatus) GetMainHeight() int64 {
if m != nil {
return m.MainHeight
......@@ -4419,195 +4426,194 @@ func init() {
proto.RegisterType((*BlsPubKey)(nil), "types.BlsPubKey")
}
func init() {
proto.RegisterFile("paracross.proto", fileDescriptor_6a397e38c9ea6747)
}
func init() { proto.RegisterFile("paracross.proto", fileDescriptor_6a397e38c9ea6747) }
var fileDescriptor_6a397e38c9ea6747 = []byte{
// 2919 bytes of a gzipped FileDescriptorProto
// 2934 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0xcd, 0x6f, 0x24, 0x47,
0xf5, 0xee, 0xf9, 0xf2, 0xcc, 0xb3, 0xc7, 0x1f, 0x1d, 0xaf, 0x33, 0x71, 0x92, 0x95, 0xd5, 0xca,
0x2f, 0xf2, 0x8f, 0x6c, 0x76, 0xb3, 0x4e, 0x08, 0x8a, 0x10, 0x82, 0xd8, 0xbb, 0xc9, 0x58, 0x59,
0x47, 0x9b, 0xb2, 0x03, 0x87, 0x08, 0x44, 0x7b, 0xa6, 0x6c, 0xb7, 0x32, 0xd3, 0x3d, 0xdb, 0xd5,
0x93, 0xb5, 0x11, 0x52, 0x38, 0x00, 0x37, 0x24, 0x2e, 0x48, 0x80, 0x04, 0x17, 0xb8, 0x21, 0x71,
0xe2, 0xcc, 0x01, 0x89, 0x4b, 0xc4, 0x25, 0x1c, 0xb9, 0x71, 0x43, 0xe2, 0xc8, 0x3f, 0x80, 0xde,
0xab, 0xaa, 0xee, 0xaa, 0xea, 0x9e, 0xb1, 0x93, 0xdd, 0x0b, 0xb7, 0xa9, 0x57, 0xaf, 0xaa, 0xde,
0xf7, 0xd7, 0x34, 0xac, 0x4e, 0xc2, 0x34, 0x1c, 0xa4, 0x89, 0x10, 0xb7, 0x27, 0x69, 0x92, 0x25,
0x7e, 0x33, 0xbb, 0x9c, 0x70, 0xb1, 0xb5, 0x9e, 0xa5, 0x61, 0x2c, 0xc2, 0x41, 0x16, 0x25, 0xb1,
0xdc, 0xd9, 0x5a, 0x1e, 0x24, 0xe3, 0x71, 0xbe, 0x5a, 0x3b, 0x19, 0x25, 0x83, 0x8f, 0x07, 0xe7,
0x61, 0xa4, 0x20, 0xc1, 0x03, 0xd8, 0x7c, 0xa8, 0x2f, 0x3b, 0xca, 0xc2, 0x6c, 0x2a, 0xee, 0xf1,
0x2c, 0x8c, 0x46, 0xc2, 0xdf, 0x80, 0x66, 0x38, 0x1c, 0xa6, 0xa2, 0xe7, 0x6d, 0xd7, 0x77, 0x3a,
0x4c, 0x2e, 0xfc, 0x17, 0xa0, 0x43, 0x77, 0xf4, 0x43, 0x71, 0xde, 0xab, 0x6d, 0xd7, 0x77, 0x96,
0x59, 0x01, 0x08, 0x3e, 0x82, 0xe7, 0x9d, 0xdb, 0xf6, 0x70, 0x4f, 0x5f, 0x79, 0x13, 0x20, 0xc7,
0x95, 0xf7, 0x2e, 0x33, 0x03, 0x82, 0x97, 0x67, 0x17, 0x8c, 0x8b, 0xe9, 0x28, 0x13, 0xfa, 0xf2,
0x1c, 0x10, 0xfc, 0xba, 0x06, 0x37, 0xf2, 0xdb, 0xfb, 0x3c, 0x3a, 0x3b, 0xcf, 0xe4, 0x1b, 0xfe,
0x26, 0xb4, 0x04, 0xfd, 0xea, 0x79, 0xdb, 0xde, 0x4e, 0x93, 0xa9, 0x15, 0xb2, 0x90, 0x45, 0xd9,
0x88, 0xf7, 0x6a, 0xdb, 0x1e, 0xb2, 0x40, 0x0b, 0xc4, 0x3e, 0xa7, 0xd3, 0xbd, 0xfa, 0xb6, 0xb7,
0x53, 0x67, 0x6a, 0xe5, 0x7f, 0x0d, 0x16, 0x87, 0x92, 0xd0, 0x5e, 0x63, 0xdb, 0xdb, 0x59, 0xda,
0x7d, 0xf1, 0x36, 0x89, 0xf5, 0x76, 0xb5, 0x80, 0x98, 0xc6, 0x46, 0xb6, 0xc6, 0x61, 0x14, 0x4b,
0x92, 0x7a, 0x4d, 0xba, 0xd4, 0x80, 0xf8, 0x5b, 0xd0, 0xa6, 0x15, 0x8a, 0xac, 0xb5, 0xed, 0xed,
0x2c, 0xb3, 0x7c, 0xed, 0xbf, 0x03, 0xcb, 0x27, 0x86, 0x88, 0x7a, 0x8b, 0xf4, 0x72, 0x50, 0xfd,
0xb2, 0x29, 0x4c, 0x66, 0x9d, 0x0b, 0xfe, 0xe5, 0x41, 0xaf, 0x52, 0x38, 0x4c, 0x4c, 0x9e, 0x92,
0x7c, 0x6c, 0x36, 0x1b, 0x73, 0xd9, 0x6c, 0xd2, 0x85, 0x05, 0x9b, 0xdb, 0xb0, 0x84, 0x86, 0x18,
0x65, 0x6f, 0x93, 0x49, 0xb5, 0xc8, 0xa4, 0x4c, 0x90, 0xbf, 0x03, 0xab, 0x72, 0xb9, 0x97, 0x9b,
0xd7, 0x22, 0x61, 0xb9, 0xe0, 0xe0, 0x57, 0x1e, 0xac, 0x3a, 0x82, 0x29, 0x38, 0xf1, 0xaa, 0x39,
0xa9, 0x59, 0x9c, 0x58, 0x46, 0x5c, 0x27, 0x8d, 0x14, 0x80, 0x2f, 0xcc, 0xa7, 0xa1, 0xce, 0xe0,
0xf7, 0xa6, 0x1a, 0xf6, 0x93, 0x58, 0xf0, 0x58, 0x4c, 0xe7, 0x13, 0x89, 0xa2, 0x39, 0x2f, 0xde,
0x93, 0x94, 0x9a, 0x20, 0xff, 0x25, 0xe8, 0x0e, 0xe4, 0x55, 0x7d, 0x53, 0x2f, 0x36, 0xd0, 0xff,
0x0a, 0xac, 0x29, 0x40, 0x21, 0xc1, 0x06, 0x3d, 0x54, 0x82, 0x07, 0x7f, 0xf2, 0xc0, 0x47, 0x32,
0xdf, 0x4f, 0x86, 0x1c, 0xc5, 0xbf, 0x9f, 0xc4, 0xa7, 0xd1, 0xd9, 0x0c, 0x02, 0x57, 0xa0, 0x96,
0x4c, 0x88, 0xae, 0x2e, 0xab, 0x25, 0x13, 0x5c, 0x47, 0x43, 0xa2, 0xa1, 0xc3, 0x6a, 0xd1, 0xd0,
0xf7, 0xa1, 0x81, 0xb1, 0x41, 0x3d, 0x46, 0xbf, 0xf1, 0xa6, 0x4f, 0xc2, 0xd1, 0x94, 0x93, 0x80,
0xba, 0x4c, 0x2e, 0xa4, 0x15, 0x44, 0xb1, 0x78, 0x27, 0x4d, 0x7e, 0xc0, 0x63, 0xf2, 0x05, 0x64,
0xb5, 0x00, 0x49, 0xcd, 0x88, 0x87, 0xd3, 0x93, 0xf7, 0xf8, 0x25, 0xf9, 0x42, 0x87, 0x15, 0x80,
0xe0, 0x5b, 0x05, 0xd5, 0xdf, 0x4e, 0x32, 0x2e, 0x6d, 0x7f, 0x46, 0xa0, 0x42, 0x0a, 0x92, 0x8c,
0xcb, 0x38, 0xd2, 0x61, 0x72, 0x11, 0xfc, 0xd1, 0x83, 0x0d, 0x93, 0xf1, 0x83, 0xa1, 0xd2, 0x8d,
0x66, 0xc2, 0x33, 0x98, 0xb8, 0x09, 0x30, 0x49, 0x93, 0x49, 0x22, 0xc2, 0xd1, 0xc1, 0x50, 0xf9,
0x88, 0x01, 0x41, 0xf3, 0x7a, 0x34, 0x8d, 0xb2, 0x03, 0x2d, 0x0c, 0xb5, 0x32, 0xdc, 0xad, 0x51,
0xed, 0x6e, 0x4d, 0x53, 0xbc, 0x16, 0xcb, 0x2d, 0x97, 0xe5, 0x5f, 0xd4, 0x60, 0x4d, 0x13, 0x9c,
0x13, 0x2b, 0x35, 0xe0, 0xe5, 0x1a, 0x28, 0x1e, 0xac, 0x55, 0x3f, 0x58, 0x37, 0x1f, 0xbc, 0x09,
0x90, 0x85, 0xe9, 0x19, 0x27, 0xc7, 0x53, 0x5a, 0x33, 0x20, 0xae, 0x96, 0x9a, 0x65, 0x2d, 0xdd,
0xd1, 0xb2, 0x6d, 0x51, 0xb4, 0x7a, 0xce, 0x88, 0x56, 0xb6, 0x6e, 0x94, 0xd8, 0xd1, 0x65, 0x4e,
0xd3, 0x64, 0x4c, 0x0f, 0x4a, 0xad, 0xe6, 0x6b, 0xc3, 0x49, 0xdb, 0x65, 0x27, 0xd5, 0x72, 0xe9,
0xb8, 0x72, 0xf9, 0xb3, 0x07, 0x37, 0x18, 0x1f, 0xf0, 0x68, 0x92, 0xe9, 0x67, 0x95, 0x11, 0x57,
0x69, 0xf2, 0x2e, 0xb4, 0x06, 0xb4, 0x4b, 0x02, 0x2a, 0x53, 0x5c, 0xf8, 0x00, 0x53, 0x88, 0xfe,
0x2b, 0xd0, 0x98, 0xa4, 0xfc, 0x13, 0x12, 0xdd, 0xd2, 0xee, 0xb3, 0xce, 0x01, 0xad, 0x0a, 0x46,
0x48, 0xfe, 0x5d, 0x58, 0x1c, 0x4c, 0xd3, 0x94, 0xc7, 0x99, 0x4a, 0x1d, 0x33, 0xf1, 0x35, 0x5e,
0xf0, 0x3b, 0x0f, 0x5e, 0x74, 0x18, 0x40, 0x2a, 0x10, 0xed, 0xc3, 0xc9, 0x30, 0xcc, 0xb8, 0x25,
0x34, 0xcf, 0x11, 0xda, 0x1d, 0x45, 0x9d, 0x64, 0xe7, 0xf9, 0x0a, 0x76, 0x1c, 0x0a, 0xbf, 0x5a,
0x50, 0x58, 0xbf, 0xfa, 0x4c, 0x4e, 0xe5, 0x7f, 0x3c, 0x78, 0xd6, 0xa1, 0x92, 0xb4, 0x9b, 0xc4,
0xbc, 0x64, 0x85, 0xd5, 0xd9, 0xc4, 0xb6, 0xb6, 0x7a, 0xc9, 0xda, 0x70, 0x3f, 0xc9, 0xc2, 0x11,
0x5e, 0xad, 0x1d, 0xc6, 0x80, 0x50, 0x4d, 0x80, 0x2b, 0x7c, 0x96, 0x6c, 0xb1, 0xc9, 0x0a, 0x00,
0xc5, 0xe2, 0x44, 0x64, 0xb4, 0xd9, 0xa2, 0xcd, 0x7c, 0xed, 0xf7, 0x60, 0x11, 0xad, 0x8f, 0x89,
0x4c, 0xd9, 0x9c, 0x5e, 0xe2, 0x9b, 0xc3, 0x24, 0xe6, 0x92, 0x59, 0x32, 0xbb, 0x26, 0x33, 0x20,
0xa8, 0x9b, 0x67, 0x34, 0xbb, 0xef, 0xa6, 0xc9, 0x74, 0xf2, 0x44, 0xf1, 0x31, 0x8f, 0x4f, 0xd2,
0xd5, 0x54, 0x7c, 0xba, 0xda, 0xcb, 0xa8, 0x5a, 0x52, 0xf6, 0x2e, 0x54, 0x64, 0x30, 0x20, 0xc1,
0xbf, 0x5d, 0x2a, 0x9f, 0x4a, 0x74, 0xd8, 0x86, 0xa5, 0x42, 0x3b, 0x9a, 0x66, 0x13, 0x74, 0x0d,
0xca, 0x4d, 0xcb, 0x6d, 0xcd, 0x74, 0xf7, 0x45, 0xb7, 0xba, 0x30, 0xb8, 0x6d, 0x97, 0xb8, 0xfd,
0xcc, 0x83, 0x2d, 0xc7, 0x12, 0x4d, 0xd5, 0x54, 0x79, 0xfd, 0xae, 0xe3, 0xf5, 0x5b, 0x8e, 0xc9,
0x1b, 0xe7, 0x73, 0xb7, 0xbf, 0x6d, 0xb9, 0x7d, 0xe5, 0x09, 0xcb, 0xaf, 0xde, 0x70, 0x3d, 0x7f,
0xde, 0x91, 0xdc, 0xad, 0x7e, 0xea, 0xc1, 0x06, 0xe3, 0x8f, 0xf2, 0x4a, 0x81, 0x42, 0x44, 0x7c,
0x9a, 0xcc, 0xb6, 0xb0, 0x48, 0x27, 0x20, 0x33, 0xe3, 0xd6, 0x0d, 0x66, 0x67, 0x25, 0x1d, 0x2b,
0x8c, 0x36, 0xdd, 0x30, 0xba, 0x0f, 0x9b, 0x8c, 0x8b, 0x89, 0x45, 0x88, 0xd4, 0xf2, 0xff, 0x43,
0x3d, 0x1a, 0xca, 0x9c, 0x3a, 0x27, 0x9c, 0x21, 0x4e, 0xf0, 0x2e, 0xc6, 0x08, 0xe7, 0x12, 0x62,
0x5b, 0xf8, 0xb7, 0xcc, 0x5b, 0xe6, 0x89, 0x86, 0x2e, 0x9a, 0xc8, 0x5c, 0xb7, 0x17, 0xc5, 0xc3,
0xc3, 0x28, 0xe6, 0xe9, 0xfe, 0x78, 0x48, 0x76, 0x11, 0xc5, 0xc3, 0xb7, 0xa9, 0xa9, 0x51, 0xf5,
0xab, 0x01, 0x21, 0xfe, 0xa2, 0x78, 0xb8, 0x8f, 0xe6, 0xa7, 0x8a, 0xa7, 0x02, 0x50, 0x44, 0x1f,
0x7c, 0xcf, 0x8e, 0x3e, 0x08, 0x09, 0xfe, 0xea, 0xc1, 0xba, 0xf5, 0x24, 0x69, 0x61, 0x46, 0x31,
0x80, 0xd7, 0x1e, 0x99, 0x9e, 0x64, 0x40, 0x6c, 0x3a, 0xea, 0xf3, 0xe9, 0x68, 0xb8, 0x74, 0xe4,
0x15, 0xe9, 0x71, 0x34, 0xe6, 0xca, 0xa3, 0x0a, 0x00, 0x7a, 0x9c, 0x2c, 0x4f, 0xa5, 0xe3, 0xa8,
0xba, 0xc9, 0x00, 0x05, 0x3f, 0xf7, 0xa0, 0x67, 0x78, 0xc7, 0xd5, 0xec, 0xdc, 0xb2, 0x12, 0x48,
0xcf, 0xd0, 0x8c, 0x75, 0x56, 0x59, 0xf9, 0xae, 0x9b, 0x3d, 0x66, 0x1f, 0xc8, 0x6d, 0xbc, 0x5f,
0x14, 0x2e, 0x88, 0xf1, 0x20, 0x12, 0x94, 0xd3, 0xc5, 0x74, 0xc2, 0x53, 0x92, 0x82, 0x24, 0xa7,
0x00, 0xa0, 0x09, 0x8f, 0xf1, 0x1e, 0x5d, 0xb3, 0xa9, 0x55, 0xf0, 0x69, 0x29, 0x53, 0xea, 0x0b,
0x55, 0xa6, 0xd4, 0xb9, 0xda, 0xab, 0xcc, 0xbd, 0x1a, 0xb9, 0x9c, 0xab, 0x6b, 0xf3, 0xf1, 0x73,
0x56, 0x1e, 0xa3, 0xb7, 0x4a, 0x03, 0xb7, 0xd8, 0x79, 0x05, 0x1a, 0xa3, 0x48, 0x64, 0x57, 0xbe,
0x8b, 0x48, 0x28, 0x43, 0xdd, 0x5e, 0xd6, 0xc8, 0x1d, 0xe6, 0xc8, 0x50, 0x21, 0x06, 0x3f, 0xd1,
0xe6, 0x89, 0xaa, 0xde, 0x3d, 0x0c, 0xa3, 0xf8, 0x30, 0x9c, 0x18, 0x21, 0xd4, 0x9b, 0xdd, 0xd6,
0xd4, 0xb4, 0xab, 0x57, 0xb7, 0x35, 0xf5, 0xb9, 0x6d, 0x4d, 0xc3, 0x6e, 0xdf, 0x82, 0x7b, 0xb2,
0xf0, 0x2e, 0xc8, 0x20, 0xbb, 0xba, 0x0d, 0xcd, 0x28, 0xe3, 0x63, 0xed, 0xde, 0x16, 0x3f, 0x26,
0xc1, 0x4c, 0xa2, 0x05, 0xff, 0xac, 0xcb, 0x84, 0x95, 0x07, 0x09, 0xe5, 0x3a, 0x2f, 0x41, 0x17,
0x5f, 0x2a, 0xda, 0x16, 0x8f, 0xba, 0x2a, 0x1b, 0x88, 0x0d, 0x62, 0x01, 0x30, 0x7b, 0x25, 0x17,
0x3c, 0x23, 0xb1, 0x15, 0x52, 0x6b, 0x58, 0x52, 0x0b, 0x60, 0x79, 0x92, 0xf2, 0xe2, 0x71, 0xd9,
0xd2, 0x59, 0x30, 0x5b, 0xb2, 0x2d, 0xb7, 0x61, 0x94, 0x37, 0x20, 0x33, 0x5c, 0xf5, 0xad, 0xfa,
0x86, 0x1c, 0x46, 0x96, 0x9f, 0x23, 0xb4, 0xe5, 0x0d, 0x39, 0x00, 0x65, 0x9f, 0x5d, 0xec, 0x27,
0xd3, 0x38, 0x13, 0x54, 0xea, 0x76, 0x59, 0xbe, 0x96, 0x7b, 0x72, 0x06, 0xd2, 0x03, 0xd9, 0x6e,
0xea, 0x35, 0x96, 0x38, 0xd9, 0x85, 0x9c, 0xa6, 0x2c, 0xd1, 0xb8, 0x44, 0x2f, 0xa9, 0x67, 0x44,
0x31, 0x1f, 0xeb, 0xa3, 0xcb, 0x52, 0xa6, 0x16, 0x10, 0x29, 0x57, 0x00, 0x79, 0x49, 0x97, 0x2e,
0xb1, 0x60, 0xfe, 0x2d, 0x58, 0x8f, 0x93, 0x78, 0x9f, 0x9a, 0xf0, 0x63, 0x4d, 0xe4, 0x0a, 0x11,
0x59, 0xde, 0x08, 0xf6, 0x60, 0xfd, 0x88, 0x8f, 0x4e, 0x55, 0xeb, 0x7b, 0x94, 0x85, 0x67, 0x5c,
0xf8, 0xaf, 0xda, 0x86, 0xa2, 0x1d, 0xc5, 0x45, 0xd4, 0x76, 0xf2, 0x00, 0xd6, 0xdc, 0x2d, 0x0c,
0x81, 0x22, 0x0b, 0xd3, 0xac, 0x6f, 0x1a, 0xbe, 0x09, 0x42, 0xfd, 0xf2, 0x38, 0x3c, 0x51, 0xf5,
0x67, 0x97, 0xa9, 0x55, 0xf0, 0x0f, 0x0f, 0x36, 0xdc, 0xeb, 0xc8, 0x7c, 0xe7, 0xd7, 0x49, 0xdd,
0x3c, 0x83, 0xbe, 0x0a, 0x4d, 0x81, 0x87, 0x9c, 0x56, 0xa0, 0x4c, 0x3d, 0x61, 0x59, 0xc5, 0x4f,
0xc3, 0x29, 0x7e, 0x6e, 0x02, 0xf0, 0x0b, 0x3e, 0xb0, 0x27, 0x45, 0x05, 0xe4, 0x0b, 0x37, 0x56,
0x01, 0x87, 0xcd, 0x07, 0xc9, 0x20, 0x1c, 0x69, 0x62, 0x0a, 0xee, 0xee, 0x6a, 0xaa, 0x3d, 0xab,
0xdc, 0xaf, 0x92, 0x84, 0xa6, 0x9c, 0xac, 0xe9, 0x20, 0x1e, 0xf2, 0x0b, 0x15, 0x3d, 0xf4, 0x32,
0x78, 0x13, 0x56, 0x64, 0x9d, 0x84, 0x14, 0x54, 0x0a, 0x2f, 0x6f, 0xf8, 0x6b, 0x46, 0xc3, 0x1f,
0x04, 0xb0, 0x26, 0xcf, 0xed, 0x87, 0xf1, 0x80, 0x8f, 0xaa, 0x4e, 0x06, 0x9f, 0xab, 0x71, 0x0e,
0x91, 0x73, 0x55, 0xa1, 0x9d, 0x5d, 0xea, 0x42, 0x3b, 0xbb, 0x44, 0x69, 0x49, 0x16, 0x61, 0xae,
0x62, 0xfa, 0x0b, 0x9a, 0xc1, 0x57, 0xa0, 0x81, 0x62, 0xeb, 0x2d, 0x11, 0xfe, 0x0d, 0x85, 0x6f,
0x73, 0xd6, 0x5f, 0x60, 0x84, 0x44, 0x3d, 0x23, 0x51, 0x4d, 0xae, 0x53, 0x5c, 0xef, 0x32, 0xd4,
0x5f, 0x60, 0x0a, 0x71, 0x6f, 0x51, 0x09, 0x21, 0xf8, 0x71, 0x51, 0xac, 0x5a, 0x9a, 0x51, 0xec,
0xdd, 0xb1, 0xf2, 0xd5, 0x5c, 0xd5, 0x94, 0xba, 0xb7, 0xda, 0xd5, 0x67, 0xf2, 0xbc, 0xf5, 0xb9,
0x07, 0x2f, 0x54, 0x91, 0x31, 0xb3, 0x85, 0xcb, 0x4d, 0xbd, 0x76, 0x2d, 0x53, 0xb7, 0x7b, 0xb7,
0xfa, 0xfc, 0xde, 0xad, 0x31, 0xaf, 0x77, 0x6b, 0xce, 0xee, 0xdd, 0x5a, 0x56, 0xef, 0x16, 0x7c,
0x0a, 0xcf, 0x57, 0xb1, 0x24, 0x54, 0x29, 0x70, 0xcb, 0x12, 0x6d, 0x6f, 0x06, 0x03, 0xa2, 0x5c,
0xd7, 0xd4, 0xae, 0x38, 0x90, 0x0b, 0xf5, 0xb7, 0x1e, 0xf8, 0x8c, 0x3f, 0xfa, 0x60, 0xca, 0xd3,
0x4b, 0x44, 0x53, 0x31, 0xce, 0x9e, 0xb1, 0x16, 0xd1, 0xc3, 0xad, 0xdd, 0x37, 0xa0, 0x39, 0xc0,
0x50, 0xa9, 0xc4, 0x25, 0x17, 0x28, 0xa9, 0x61, 0x94, 0x72, 0x59, 0xe4, 0x2a, 0x49, 0xe5, 0x00,
0x23, 0x75, 0x35, 0xad, 0xd4, 0xb5, 0x01, 0xcd, 0x88, 0xdc, 0x55, 0xb6, 0xbe, 0x72, 0x11, 0x7c,
0x80, 0xd5, 0xca, 0x64, 0x74, 0xe9, 0x52, 0xf8, 0x16, 0xa5, 0x20, 0x69, 0x23, 0x2a, 0x12, 0xcf,
0x35, 0xa3, 0x02, 0x3b, 0xf8, 0x9e, 0xf1, 0x2f, 0xc1, 0xbe, 0x1a, 0xc7, 0x0a, 0x5d, 0x5b, 0x8a,
0xe8, 0x2c, 0x56, 0x29, 0x9b, 0x7e, 0xa3, 0x62, 0xa9, 0xc7, 0x3d, 0x0c, 0x65, 0x5b, 0xbc, 0xcc,
0xf2, 0x75, 0xd1, 0x0c, 0xd7, 0x8d, 0x61, 0x5d, 0xf0, 0x43, 0x63, 0xb2, 0x2f, 0xef, 0x57, 0xd5,
0xfd, 0xae, 0x25, 0x55, 0xbb, 0x85, 0x70, 0xca, 0x88, 0x5c, 0xe2, 0x77, 0xa0, 0x7e, 0x32, 0x12,
0x4a, 0xa1, 0xa5, 0x19, 0xbe, 0x45, 0x3e, 0x43, 0xcc, 0x20, 0x93, 0x33, 0x41, 0xda, 0xa6, 0x22,
0xec, 0x09, 0x1e, 0xdf, 0x81, 0xd5, 0x48, 0x18, 0xe2, 0x54, 0xd9, 0xa4, 0xcd, 0x5c, 0x70, 0xf0,
0x4b, 0x0f, 0xfc, 0x7d, 0xbc, 0xe5, 0x6d, 0x21, 0x78, 0x76, 0x9c, 0x86, 0xb1, 0x38, 0xe5, 0x29,
0x5a, 0x42, 0x88, 0x80, 0xfb, 0x17, 0x7c, 0xa0, 0x4b, 0xe4, 0x1c, 0x80, 0x69, 0x90, 0x16, 0x47,
0x97, 0xe3, 0x93, 0x64, 0xa4, 0xcc, 0xca, 0x04, 0xa1, 0xad, 0x84, 0xe3, 0xdc, 0xc0, 0xea, 0x4c,
0xad, 0x10, 0x9e, 0x25, 0x46, 0x52, 0x52, 0x2b, 0x54, 0x60, 0xac, 0x3d, 0xb0, 0xc3, 0xe8, 0x77,
0xf0, 0x9b, 0x96, 0x31, 0x61, 0x57, 0xc2, 0x78, 0x13, 0x9b, 0x69, 0x14, 0x9d, 0x12, 0xc6, 0x0b,
0xd5, 0x82, 0x95, 0xd8, 0x14, 0x13, 0x69, 0xed, 0xbf, 0x0e, 0x4d, 0x2a, 0xe3, 0x2b, 0xc6, 0x4e,
0xae, 0xc0, 0x31, 0x50, 0x13, 0xae, 0xff, 0x0d, 0xe8, 0x86, 0xa6, 0x54, 0x54, 0x6f, 0xad, 0x23,
0x36, 0x49, 0x4c, 0xe8, 0xcd, 0xfe, 0x02, 0xb3, 0xb1, 0xf3, 0xe3, 0xdf, 0x89, 0xb2, 0xf3, 0x61,
0x1a, 0x3e, 0x26, 0xe6, 0xdc, 0xe3, 0x7a, 0x33, 0x3f, 0xae, 0x01, 0xfe, 0xeb, 0xd0, 0xce, 0xf4,
0xc3, 0xad, 0xf9, 0x0f, 0xe7, 0x88, 0x78, 0xe8, 0xb1, 0x7e, 0x6e, 0x71, 0xfe, 0x73, 0x39, 0xa2,
0x7f, 0x1f, 0x56, 0xf4, 0x05, 0xc7, 0x09, 0x69, 0xbc, 0x6d, 0x49, 0xc9, 0x7e, 0x4f, 0xa2, 0xf4,
0x17, 0x98, 0x73, 0xc8, 0xff, 0x3a, 0x40, 0x9c, 0x0f, 0x40, 0xa9, 0x80, 0x9c, 0x37, 0xe2, 0xec,
0x2f, 0x30, 0x03, 0xdd, 0x7f, 0x07, 0x56, 0x63, 0x7b, 0x18, 0xa2, 0xf2, 0xe9, 0x9c, 0x71, 0x49,
0x7f, 0x81, 0xb9, 0x87, 0xfc, 0x3d, 0x58, 0x15, 0x3a, 0xd8, 0xa8, 0x7b, 0x64, 0x9e, 0xdd, 0x34,
0xee, 0x31, 0x76, 0xf1, 0x0e, 0xe7, 0x80, 0xff, 0x1e, 0xf8, 0x83, 0x92, 0x4b, 0xa8, 0xfc, 0xab,
0x19, 0x2a, 0xfb, 0x4c, 0x7f, 0x81, 0x55, 0x1c, 0xf3, 0xbf, 0x09, 0xdd, 0x89, 0xd9, 0x5a, 0xf5,
0xba, 0xa5, 0x36, 0xcd, 0x9c, 0x34, 0xa0, 0x1d, 0x58, 0xf8, 0x46, 0xbd, 0xd1, 0xc4, 0x7a, 0xa3,
0x48, 0xef, 0x9f, 0x79, 0xb0, 0x69, 0x74, 0xa4, 0x86, 0xf9, 0xcf, 0x9a, 0x43, 0x19, 0x85, 0xe5,
0xf5, 0xe2, 0xc8, 0x6b, 0xd6, 0x1c, 0xaa, 0xe4, 0x6c, 0xd6, 0x3f, 0x7c, 0x32, 0x97, 0xbd, 0xe9,
0x4e, 0xa2, 0xe6, 0x1f, 0xca, 0xf3, 0xd9, 0x7b, 0xd6, 0x20, 0xbd, 0xf0, 0xc9, 0x2f, 0x13, 0xfe,
0x82, 0x1f, 0x35, 0x30, 0xf9, 0xd8, 0xb7, 0x51, 0xa5, 0x61, 0x97, 0x0a, 0x5e, 0xa9, 0x54, 0xd8,
0x86, 0x25, 0x5a, 0x49, 0x31, 0x2a, 0xa1, 0x9b, 0x20, 0xff, 0x65, 0x58, 0xc1, 0xf2, 0xe0, 0x28,
0x1c, 0x73, 0x85, 0x24, 0x33, 0xa8, 0x03, 0x2d, 0x6a, 0xc7, 0x46, 0x75, 0xf7, 0xd7, 0x74, 0x7b,
0xe6, 0xa2, 0x2f, 0x6b, 0xcd, 0xeb, 0xcb, 0x16, 0xe7, 0xf4, 0x65, 0x6d, 0xa7, 0x2f, 0xb3, 0xfa,
0xc5, 0x8e, 0xdb, 0x2f, 0x1a, 0x5d, 0x1b, 0x5c, 0xd1, 0xb5, 0x2d, 0x5d, 0xa7, 0x6b, 0x5b, 0xae,
0xe8, 0xda, 0x4a, 0x3d, 0x75, 0xf7, 0x9a, 0x3d, 0xf5, 0x4a, 0x75, 0x4f, 0xbd, 0x03, 0xab, 0xf4,
0x97, 0xe4, 0xfd, 0xa2, 0x7d, 0x59, 0x95, 0x98, 0x0e, 0x38, 0xf8, 0x7e, 0xd9, 0x37, 0x18, 0x1f,
0x24, 0xe9, 0xf0, 0x69, 0xf9, 0x46, 0xf0, 0x7f, 0xb0, 0x94, 0x6f, 0x1f, 0x5f, 0x50, 0x66, 0xbb,
0xc8, 0xe7, 0x06, 0x98, 0xd9, 0x68, 0x25, 0xc7, 0x92, 0xc5, 0x8c, 0xf5, 0x18, 0xed, 0xc0, 0x9d,
0x10, 0x5c, 0xe7, 0xef, 0xe2, 0xe0, 0x0f, 0x35, 0x58, 0xb7, 0x06, 0x9c, 0xff, 0x5b, 0x16, 0xdd,
0xf9, 0xb2, 0x16, 0xdd, 0x31, 0x2c, 0xba, 0x42, 0xff, 0x9d, 0x6a, 0xfd, 0xbf, 0x0b, 0xcf, 0x58,
0xc2, 0x22, 0xb9, 0x63, 0x40, 0x6b, 0x11, 0xdd, 0xee, 0xb4, 0xa8, 0x24, 0x58, 0xa6, 0xf0, 0x64,
0x60, 0x72, 0xf5, 0x87, 0x3c, 0x54, 0x6b, 0xaf, 0x34, 0xfd, 0xb2, 0xbe, 0x4c, 0xf9, 0x5b, 0x0d,
0x56, 0x8a, 0x92, 0x06, 0xf3, 0x04, 0x9a, 0x23, 0x36, 0xe6, 0xda, 0x1c, 0xf1, 0x37, 0x85, 0xfc,
0x44, 0x57, 0xeb, 0x59, 0x82, 0x4a, 0x8e, 0xf2, 0xd4, 0x4d, 0xea, 0x69, 0x33, 0x03, 0x62, 0xd8,
0x5e, 0xc3, 0xb4, 0x3d, 0xa3, 0x0a, 0x6b, 0x5a, 0x55, 0x98, 0x0f, 0x0d, 0x6c, 0xf7, 0x95, 0x5e,
0xe8, 0x37, 0x75, 0x0e, 0xb2, 0x9c, 0x93, 0x7f, 0x53, 0xa9, 0x15, 0x32, 0x24, 0x19, 0xbf, 0x9c,
0x70, 0xd2, 0x47, 0x97, 0x15, 0x00, 0x43, 0xfd, 0x60, 0xa9, 0x9f, 0x3e, 0x03, 0x40, 0xb3, 0x41,
0x59, 0x2a, 0x4d, 0xdd, 0x20, 0x8c, 0x12, 0x9c, 0xfe, 0xe0, 0x0e, 0xd3, 0x50, 0x61, 0x6d, 0xca,
0x71, 0x44, 0x01, 0xc1, 0x40, 0x25, 0xa6, 0x83, 0x01, 0x17, 0xa2, 0xf7, 0x2c, 0xb1, 0xae, 0x97,
0xc1, 0xdf, 0x3d, 0x39, 0xdd, 0xa5, 0xe1, 0xc3, 0xbd, 0x13, 0x8a, 0x14, 0x33, 0xe7, 0x92, 0xe6,
0x64, 0xb1, 0xe6, 0x7c, 0xff, 0x72, 0xd5, 0x54, 0xf2, 0x65, 0x58, 0x99, 0x84, 0x98, 0xa7, 0x0e,
0xcd, 0xd9, 0xe4, 0x32, 0x73, 0xa0, 0x57, 0x0c, 0xd0, 0x5f, 0x82, 0x7a, 0x76, 0x21, 0x3f, 0x3b,
0x59, 0xda, 0xf5, 0x95, 0xe5, 0x1d, 0x17, 0x1f, 0x4b, 0x31, 0xdc, 0x0e, 0xfe, 0xa2, 0x3e, 0x0e,
0x93, 0xb5, 0x11, 0x52, 0x38, 0x00, 0x37, 0x24, 0x2e, 0x48, 0x70, 0x80, 0x0b, 0xdc, 0x90, 0x38,
0x71, 0xe6, 0x80, 0xc4, 0x25, 0xe2, 0x12, 0x8e, 0xdc, 0xb8, 0x81, 0x38, 0xf2, 0x0f, 0xa0, 0xf7,
0xaa, 0xaa, 0xbb, 0xaa, 0xba, 0x67, 0xec, 0x64, 0xf7, 0xc2, 0xad, 0xeb, 0xf5, 0xab, 0xaa, 0xf7,
0xfd, 0xd5, 0x0d, 0xab, 0x93, 0x30, 0x0d, 0x07, 0x69, 0x22, 0xc4, 0xed, 0x49, 0x9a, 0x64, 0x89,
0xdf, 0xcc, 0x2e, 0x27, 0x5c, 0x6c, 0xad, 0x67, 0x69, 0x18, 0x8b, 0x70, 0x90, 0x45, 0x49, 0x2c,
0xdf, 0x6c, 0x2d, 0x0f, 0x92, 0xf1, 0x38, 0x5f, 0xad, 0x9d, 0x8c, 0x92, 0xc1, 0xc7, 0x83, 0xf3,
0x30, 0x52, 0x90, 0xe0, 0x01, 0x6c, 0x3e, 0xd4, 0x87, 0x1d, 0x65, 0x61, 0x36, 0x15, 0xf7, 0x78,
0x16, 0x46, 0x23, 0xe1, 0x6f, 0x40, 0x33, 0x1c, 0x0e, 0x53, 0xd1, 0xf3, 0xb6, 0xeb, 0x3b, 0x1d,
0x26, 0x17, 0xfe, 0x0b, 0xd0, 0xa1, 0x33, 0xfa, 0xa1, 0x38, 0xef, 0xd5, 0xb6, 0xeb, 0x3b, 0xcb,
0xac, 0x00, 0x04, 0x1f, 0xc1, 0xf3, 0xce, 0x69, 0x7b, 0xf8, 0x4e, 0x1f, 0x79, 0x13, 0x20, 0xc7,
0x95, 0xe7, 0x2e, 0x33, 0x03, 0x82, 0x87, 0x67, 0x17, 0x8c, 0x8b, 0xe9, 0x28, 0x13, 0xfa, 0xf0,
0x1c, 0x10, 0xfc, 0xab, 0x06, 0x37, 0xf2, 0xd3, 0xfb, 0x3c, 0x3a, 0x3b, 0xcf, 0xe4, 0x1d, 0xfe,
0x26, 0xb4, 0x04, 0x3d, 0xf5, 0xbc, 0x6d, 0x6f, 0xa7, 0xc9, 0xd4, 0x0a, 0x59, 0xc8, 0xa2, 0x6c,
0xc4, 0x7b, 0xb5, 0x6d, 0x0f, 0x59, 0xa0, 0x05, 0x62, 0x9f, 0xd3, 0xee, 0x5e, 0x7d, 0xdb, 0xdb,
0xa9, 0x33, 0xb5, 0xf2, 0xbf, 0x06, 0x8b, 0x43, 0x49, 0x68, 0xaf, 0xb1, 0xed, 0xed, 0x2c, 0xed,
0xbe, 0x78, 0x9b, 0xc4, 0x7a, 0xbb, 0x5a, 0x40, 0x4c, 0x63, 0xfb, 0x87, 0xe0, 0x8b, 0xe9, 0x84,
0xa7, 0x9f, 0x44, 0x22, 0x4a, 0x62, 0xf5, 0xba, 0xd7, 0xbc, 0xce, 0x19, 0x15, 0x1b, 0x51, 0x4a,
0xe3, 0x30, 0x8a, 0x25, 0x87, 0xbd, 0x16, 0xd1, 0x68, 0x40, 0xfc, 0x2d, 0x68, 0xd3, 0x0a, 0x35,
0xb0, 0xb8, 0xed, 0xed, 0x2c, 0xb3, 0x7c, 0xed, 0xbf, 0x03, 0xcb, 0x27, 0x86, 0xc4, 0x7b, 0x6d,
0x22, 0x22, 0xa8, 0x26, 0xc2, 0xd4, 0x0d, 0xb3, 0xf6, 0x05, 0xff, 0xf4, 0xa0, 0x57, 0x29, 0x6b,
0x26, 0x26, 0x4f, 0x49, 0xdc, 0x36, 0x9b, 0x8d, 0xb9, 0x6c, 0x36, 0xe9, 0xc0, 0x82, 0xcd, 0x6d,
0x58, 0x42, 0xbb, 0x8e, 0xb2, 0xb7, 0xc9, 0x42, 0x5b, 0x64, 0xa1, 0x26, 0xc8, 0xdf, 0x81, 0x55,
0xb9, 0xdc, 0xcb, 0xad, 0x75, 0x91, 0xb0, 0x5c, 0x70, 0xf0, 0x2b, 0x0f, 0x56, 0x1d, 0xc1, 0x14,
0x9c, 0x78, 0xd5, 0x9c, 0xd4, 0x2c, 0x4e, 0x2c, 0x9f, 0xa8, 0x93, 0x46, 0x0a, 0xc0, 0x17, 0xe6,
0xd3, 0x50, 0x67, 0xf0, 0x3b, 0x53, 0x0d, 0xfb, 0x49, 0x2c, 0x78, 0x2c, 0xa6, 0xf3, 0x89, 0x44,
0xd1, 0x9c, 0x17, 0xf7, 0x49, 0x4a, 0x4d, 0x90, 0xff, 0x12, 0x74, 0x07, 0xf2, 0xa8, 0xbe, 0xa9,
0x17, 0x1b, 0xe8, 0x7f, 0x05, 0xd6, 0x14, 0xa0, 0x90, 0x60, 0x83, 0x2e, 0x2a, 0xc1, 0x83, 0x3f,
0x7a, 0xe0, 0x23, 0x99, 0xef, 0x27, 0x43, 0x8e, 0xe2, 0xdf, 0x4f, 0xe2, 0xd3, 0xe8, 0x6c, 0x06,
0x81, 0x2b, 0x50, 0x4b, 0x26, 0x44, 0x57, 0x97, 0xd5, 0x92, 0x09, 0xae, 0xa3, 0x21, 0xd1, 0xd0,
0x61, 0xb5, 0x68, 0xe8, 0xfb, 0xd0, 0xc0, 0x50, 0xa3, 0x2e, 0xa3, 0x67, 0x3c, 0xe9, 0x93, 0x70,
0x34, 0xe5, 0x24, 0xa0, 0x2e, 0x93, 0x0b, 0x69, 0x05, 0x51, 0x2c, 0xde, 0x49, 0x93, 0x1f, 0xf0,
0x58, 0x79, 0x8a, 0x09, 0x92, 0x9a, 0x11, 0x0f, 0xa7, 0x27, 0xef, 0xf1, 0x4b, 0xf2, 0x95, 0x0e,
0x2b, 0x00, 0xc1, 0xb7, 0x0a, 0xaa, 0xbf, 0x9d, 0x64, 0x5c, 0xda, 0xfe, 0x8c, 0xb8, 0x87, 0x14,
0x24, 0x19, 0x97, 0x61, 0xa9, 0xc3, 0xe4, 0x22, 0xf8, 0x83, 0x07, 0x1b, 0x26, 0xe3, 0x07, 0x43,
0xa5, 0x1b, 0xcd, 0x84, 0x67, 0x30, 0x71, 0x13, 0x60, 0x92, 0x26, 0x93, 0x44, 0x84, 0xa3, 0x83,
0xa1, 0xf2, 0x11, 0x03, 0x82, 0xe6, 0xf5, 0x68, 0x1a, 0x65, 0x07, 0x5a, 0x18, 0x6a, 0x65, 0xb8,
0x5b, 0xa3, 0xda, 0xdd, 0x9a, 0xa6, 0x78, 0x2d, 0x96, 0x5b, 0x2e, 0xcb, 0xbf, 0xa8, 0xc1, 0x9a,
0x26, 0x38, 0x27, 0x56, 0x6a, 0xc0, 0xcb, 0x35, 0x50, 0x5c, 0x58, 0xab, 0xbe, 0xb0, 0x6e, 0x5e,
0x78, 0x13, 0x20, 0x0b, 0xd3, 0x33, 0x4e, 0x8e, 0xa7, 0xb4, 0x66, 0x40, 0x5c, 0x2d, 0x35, 0xcb,
0x5a, 0xba, 0xa3, 0x65, 0xdb, 0xa2, 0x68, 0xf5, 0x9c, 0x11, 0xad, 0x6c, 0xdd, 0x28, 0xb1, 0xa3,
0xcb, 0x9c, 0xa6, 0xc9, 0x98, 0x2e, 0x94, 0x5a, 0xcd, 0xd7, 0x86, 0x93, 0xb6, 0xcb, 0x4e, 0xaa,
0xe5, 0xd2, 0x71, 0xe5, 0xf2, 0x27, 0x0f, 0x6e, 0x30, 0x3e, 0xe0, 0xd1, 0x24, 0xd3, 0xd7, 0x2a,
0x23, 0xae, 0xd2, 0xe4, 0x5d, 0x68, 0x0d, 0xe8, 0x2d, 0x09, 0xa8, 0x4c, 0x71, 0xe1, 0x03, 0x4c,
0x21, 0xfa, 0xaf, 0x40, 0x63, 0x92, 0xf2, 0x4f, 0x48, 0x74, 0x4b, 0xbb, 0xcf, 0x3a, 0x1b, 0xb4,
0x2a, 0x18, 0x21, 0xf9, 0x77, 0x61, 0x71, 0x30, 0x4d, 0x53, 0x1e, 0x67, 0x2a, 0x13, 0xcd, 0xc4,
0xd7, 0x78, 0xc1, 0x6f, 0x3d, 0x78, 0xd1, 0x61, 0x00, 0xa9, 0x40, 0xb4, 0x0f, 0x27, 0xc3, 0x30,
0xe3, 0x96, 0xd0, 0x3c, 0x47, 0x68, 0x77, 0x14, 0x75, 0x92, 0x9d, 0xe7, 0x2b, 0xd8, 0x71, 0x28,
0xfc, 0x6a, 0x41, 0x61, 0xfd, 0xea, 0x3d, 0x39, 0x95, 0xff, 0xf1, 0xe0, 0x59, 0x87, 0x4a, 0xd2,
0x6e, 0x12, 0xf3, 0x92, 0x15, 0x56, 0x67, 0x13, 0xdb, 0xda, 0xea, 0x25, 0x6b, 0xc3, 0xf7, 0x49,
0x16, 0x8e, 0xf0, 0x68, 0xed, 0x30, 0x06, 0x84, 0x4a, 0x0c, 0x5c, 0xe1, 0xb5, 0x64, 0x8b, 0x4d,
0x56, 0x00, 0x28, 0x16, 0x27, 0x22, 0xa3, 0x97, 0x2d, 0x7a, 0x99, 0xaf, 0xfd, 0x1e, 0x2c, 0xa2,
0xf5, 0x31, 0x91, 0x29, 0x9b, 0xd3, 0x4b, 0xbc, 0x73, 0x98, 0xc4, 0x5c, 0x32, 0x4b, 0x66, 0xd7,
0x64, 0x06, 0x04, 0x75, 0xf3, 0x8c, 0x66, 0xf7, 0xdd, 0x34, 0x99, 0x4e, 0x9e, 0x28, 0x3e, 0xe6,
0xf1, 0x49, 0xba, 0x9a, 0x8a, 0x4f, 0x57, 0x7b, 0x19, 0x15, 0x5f, 0xca, 0xde, 0x85, 0x8a, 0x0c,
0x06, 0x24, 0xf8, 0xb7, 0x4b, 0xe5, 0x53, 0x89, 0x0e, 0xdb, 0xb0, 0x54, 0x68, 0x47, 0xd3, 0x6c,
0x82, 0xae, 0x41, 0xb9, 0x69, 0xb9, 0xad, 0x99, 0xee, 0xbe, 0xe8, 0x56, 0x17, 0x06, 0xb7, 0xed,
0x12, 0xb7, 0x9f, 0x79, 0xb0, 0xe5, 0x58, 0xa2, 0xa9, 0x9a, 0x2a, 0xaf, 0xdf, 0x75, 0xbc, 0x7e,
0xcb, 0x31, 0x79, 0x63, 0x7f, 0xee, 0xf6, 0xb7, 0x2d, 0xb7, 0xaf, 0xdc, 0x61, 0xf9, 0xd5, 0x1b,
0xae, 0xe7, 0xcf, 0xdb, 0x92, 0xbb, 0xd5, 0x4f, 0x3d, 0xd8, 0x60, 0xfc, 0x51, 0x5e, 0x29, 0x50,
0x88, 0x88, 0x4f, 0x93, 0xd9, 0x16, 0x16, 0xe9, 0x04, 0x64, 0x66, 0xdc, 0xba, 0xc1, 0xec, 0xac,
0xa4, 0x63, 0x85, 0xd1, 0xa6, 0x1b, 0x46, 0xf7, 0x61, 0x93, 0x71, 0x31, 0xb1, 0x08, 0x91, 0x5a,
0xfe, 0x7f, 0xa8, 0x47, 0x43, 0x99, 0x53, 0xe7, 0x84, 0x33, 0xc4, 0x09, 0xde, 0xc5, 0x18, 0xe1,
0x1c, 0x42, 0x6c, 0x0b, 0xff, 0x96, 0x79, 0xca, 0x3c, 0xd1, 0xd0, 0x41, 0x13, 0x99, 0xeb, 0xf6,
0xa2, 0x78, 0x78, 0x18, 0xc5, 0x3c, 0xdd, 0x1f, 0x0f, 0xc9, 0x2e, 0xa2, 0x78, 0xf8, 0x36, 0xf5,
0x48, 0xaa, 0x7e, 0x35, 0x20, 0xc4, 0x5f, 0x14, 0x0f, 0xf7, 0xd1, 0xfc, 0x54, 0xf1, 0x54, 0x00,
0x8a, 0xe8, 0x83, 0xf7, 0xd9, 0xd1, 0x07, 0x21, 0xc1, 0x5f, 0x3c, 0x58, 0xb7, 0xae, 0x24, 0x2d,
0xcc, 0x28, 0x06, 0xf0, 0xd8, 0x23, 0xd3, 0x93, 0x0c, 0x88, 0x4d, 0x47, 0x7d, 0x3e, 0x1d, 0x0d,
0x97, 0x8e, 0xbc, 0x22, 0x3d, 0x8e, 0xc6, 0x5c, 0x79, 0x54, 0x01, 0x40, 0x8f, 0x93, 0xe5, 0xa9,
0xd9, 0x61, 0x98, 0xa0, 0xe0, 0xe7, 0x1e, 0xf4, 0x0c, 0xef, 0xb8, 0x9a, 0x9d, 0x5b, 0x56, 0x02,
0xe9, 0x19, 0x9a, 0xb1, 0xf6, 0x2a, 0x2b, 0xdf, 0x75, 0xb3, 0xc7, 0xec, 0x0d, 0xb9, 0x8d, 0xf7,
0x8b, 0xc2, 0x05, 0x31, 0x1e, 0x44, 0x82, 0x72, 0x3a, 0xf5, 0x4f, 0x24, 0x05, 0x49, 0x4e, 0x01,
0x40, 0x13, 0x1e, 0xe3, 0x39, 0xba, 0x66, 0x53, 0xab, 0xe0, 0xd3, 0x52, 0xa6, 0xd4, 0x07, 0xaa,
0x4c, 0xa9, 0x73, 0xb5, 0x57, 0x99, 0x7b, 0x35, 0x72, 0x39, 0x57, 0xd7, 0xe6, 0xe3, 0xe7, 0xac,
0x3c, 0x46, 0x6f, 0x95, 0x06, 0x6e, 0xb1, 0xf3, 0x0a, 0x34, 0x46, 0x91, 0xc8, 0xae, 0xbc, 0x17,
0x91, 0x50, 0x86, 0xba, 0x5b, 0xad, 0x91, 0x3b, 0xcc, 0x91, 0xa1, 0x42, 0x0c, 0x7e, 0xa2, 0xcd,
0x13, 0x55, 0xbd, 0x7b, 0x18, 0x46, 0xf1, 0x61, 0x38, 0x31, 0x42, 0xa8, 0x37, 0xbb, 0xad, 0xa9,
0x69, 0x57, 0xaf, 0x6e, 0x6b, 0xea, 0x73, 0xdb, 0x9a, 0x86, 0xdd, 0xbe, 0x05, 0xf7, 0x64, 0xe1,
0x5d, 0x90, 0x41, 0x76, 0x75, 0x1b, 0x9a, 0x51, 0xc6, 0xc7, 0xda, 0xbd, 0x2d, 0x7e, 0x4c, 0x82,
0x99, 0x44, 0x0b, 0xfe, 0x51, 0x97, 0x09, 0x2b, 0x0f, 0x12, 0xca, 0x75, 0x5e, 0x82, 0x2e, 0xde,
0x54, 0xb4, 0x2d, 0x1e, 0x75, 0x55, 0x36, 0x10, 0x1b, 0xc4, 0x02, 0x60, 0xf6, 0x4a, 0x2e, 0x78,
0x46, 0x62, 0x2b, 0xa4, 0xd6, 0xb0, 0xa4, 0x16, 0xc0, 0xf2, 0x24, 0xe5, 0xc5, 0xe5, 0xb2, 0xa5,
0xb3, 0x60, 0xb6, 0x64, 0x5b, 0x6e, 0xc3, 0x28, 0x4f, 0x40, 0x66, 0xb8, 0xd1, 0xe3, 0x5b, 0x30,
0xb2, 0xfc, 0x1c, 0xa1, 0x2d, 0x4f, 0xc8, 0x01, 0x28, 0xfb, 0xec, 0x62, 0x3f, 0x99, 0xc6, 0x99,
0xa0, 0x52, 0xb7, 0xcb, 0xf2, 0xb5, 0x7c, 0x27, 0x47, 0x2a, 0x3d, 0x90, 0xed, 0xa6, 0x5e, 0x63,
0x89, 0x93, 0x5d, 0xc8, 0xe1, 0xcc, 0x12, 0x4d, 0x5f, 0xf4, 0x92, 0x7a, 0x46, 0x14, 0xf3, 0xb1,
0xde, 0xba, 0x2c, 0x65, 0x6a, 0x01, 0x91, 0x72, 0x05, 0x90, 0x87, 0x74, 0xe9, 0x10, 0x0b, 0xe6,
0xdf, 0x82, 0xf5, 0x38, 0x89, 0xf7, 0xa9, 0x09, 0x3f, 0xd6, 0x44, 0xae, 0x10, 0x91, 0xe5, 0x17,
0xc1, 0x1e, 0xac, 0x1f, 0xf1, 0xd1, 0xa9, 0x6a, 0x7d, 0x8f, 0xb2, 0xf0, 0x8c, 0x0b, 0xff, 0x55,
0xdb, 0x50, 0xb4, 0xa3, 0xb8, 0x88, 0xda, 0x4e, 0x1e, 0xc0, 0x9a, 0xfb, 0x0a, 0x43, 0xa0, 0xc8,
0xc2, 0x34, 0xeb, 0x9b, 0x86, 0x6f, 0x82, 0x50, 0xbf, 0x3c, 0x0e, 0x4f, 0x54, 0xfd, 0xd9, 0x65,
0x6a, 0x15, 0xfc, 0xdd, 0x83, 0x0d, 0xf7, 0x38, 0x32, 0xdf, 0xf9, 0x75, 0x52, 0x37, 0xcf, 0xa0,
0xaf, 0x42, 0x53, 0xe0, 0x26, 0xa7, 0x15, 0x28, 0x53, 0x4f, 0x58, 0x56, 0xf1, 0xd3, 0x70, 0x8a,
0x9f, 0x9b, 0x00, 0xfc, 0x82, 0x0f, 0x14, 0x13, 0x32, 0xce, 0x1b, 0x90, 0x2f, 0xdc, 0x58, 0x05,
0x1c, 0x36, 0x1f, 0x24, 0x83, 0x70, 0xa4, 0x89, 0x29, 0xb8, 0xbb, 0xab, 0xa9, 0xf6, 0xac, 0x72,
0xbf, 0x4a, 0x12, 0x9a, 0x72, 0xb2, 0xa6, 0x83, 0x78, 0xc8, 0x2f, 0x54, 0xf4, 0xd0, 0xcb, 0xe0,
0x4d, 0x58, 0x91, 0x75, 0x12, 0x52, 0x50, 0x29, 0xbc, 0xbc, 0xe1, 0xaf, 0x19, 0x0d, 0x7f, 0x10,
0xc0, 0x9a, 0xdc, 0xb7, 0x1f, 0xc6, 0x03, 0x3e, 0xaa, 0xda, 0x19, 0x7c, 0xae, 0xc6, 0x39, 0x44,
0xce, 0x55, 0x85, 0x76, 0x76, 0xa9, 0x0b, 0xed, 0xec, 0x12, 0xa5, 0x25, 0x59, 0x84, 0xb9, 0x8a,
0xe9, 0x2f, 0x68, 0x06, 0x5f, 0x81, 0x06, 0x8a, 0xad, 0xb7, 0x44, 0xf8, 0x37, 0x14, 0xbe, 0xcd,
0x59, 0x7f, 0x81, 0x11, 0x12, 0xf5, 0x8c, 0x44, 0x35, 0xb9, 0x4e, 0x71, 0xbc, 0xcb, 0x50, 0x7f,
0x81, 0x29, 0xc4, 0xbd, 0x45, 0x25, 0x84, 0xe0, 0xc7, 0x45, 0xb1, 0x6a, 0x69, 0x46, 0xb1, 0x77,
0xc7, 0xca, 0x57, 0x73, 0x55, 0x53, 0xea, 0xde, 0x6a, 0x57, 0xef, 0xc9, 0xf3, 0xd6, 0xe7, 0x1e,
0xbc, 0x50, 0x45, 0xc6, 0xcc, 0x16, 0x2e, 0x37, 0xf5, 0xda, 0xb5, 0x4c, 0xdd, 0xee, 0xdd, 0xea,
0xf3, 0x7b, 0xb7, 0xc6, 0xbc, 0xde, 0xad, 0x39, 0xbb, 0x77, 0x6b, 0x59, 0xbd, 0x5b, 0xf0, 0x29,
0x3c, 0x5f, 0xc5, 0x92, 0x50, 0xa5, 0xc0, 0x2d, 0x4b, 0xb4, 0xbd, 0x19, 0x0c, 0x88, 0x72, 0x5d,
0x53, 0xbb, 0x62, 0x43, 0x2e, 0xd4, 0xdf, 0x78, 0xe0, 0x33, 0xfe, 0xe8, 0x83, 0x29, 0x4f, 0x2f,
0x11, 0x4d, 0xc5, 0x38, 0x7b, 0xc6, 0x5a, 0x44, 0x0f, 0xb7, 0x76, 0xdf, 0x80, 0xe6, 0x00, 0x43,
0xa5, 0x12, 0x97, 0x5c, 0xa0, 0xa4, 0x86, 0x51, 0xca, 0x65, 0x91, 0xab, 0x24, 0x95, 0x03, 0x8c,
0xd4, 0xd5, 0xb4, 0x52, 0xd7, 0x06, 0x34, 0x23, 0x72, 0x57, 0xd9, 0xfa, 0xca, 0x45, 0xf0, 0x01,
0x56, 0x2b, 0x93, 0xd1, 0xa5, 0x4b, 0xe1, 0x5b, 0x94, 0x82, 0xa4, 0x8d, 0xa8, 0x48, 0x3c, 0xd7,
0x8c, 0x0a, 0xec, 0xe0, 0x7b, 0xc6, 0x47, 0x87, 0x7d, 0x35, 0x8e, 0x15, 0xba, 0xb6, 0x14, 0xd1,
0x59, 0xac, 0x52, 0x36, 0x3d, 0xa3, 0x62, 0xa9, 0xc7, 0x3d, 0x0c, 0x65, 0x5b, 0xbc, 0xcc, 0xf2,
0x75, 0xd1, 0x0c, 0xd7, 0x8d, 0x61, 0x5d, 0xf0, 0x43, 0xe3, 0x43, 0x81, 0x3c, 0x5f, 0x55, 0xf7,
0xbb, 0x96, 0x54, 0xed, 0x16, 0xc2, 0x29, 0x23, 0x72, 0x89, 0xdf, 0x81, 0xfa, 0xc9, 0x48, 0x28,
0x85, 0x96, 0xc6, 0xf9, 0x16, 0xf9, 0x0c, 0x31, 0x83, 0x4c, 0xce, 0x04, 0xe9, 0x35, 0x15, 0x61,
0x4f, 0x70, 0xf9, 0x0e, 0xac, 0x46, 0xc2, 0x10, 0xa7, 0xca, 0x26, 0x6d, 0xe6, 0x82, 0x83, 0x5f,
0x7a, 0xe0, 0xef, 0xe3, 0x29, 0x6f, 0x0b, 0xc1, 0xb3, 0xe3, 0x34, 0x8c, 0xc5, 0x29, 0x4f, 0xd1,
0x12, 0x42, 0x04, 0xdc, 0xbf, 0xe0, 0x03, 0x5d, 0x22, 0xe7, 0x00, 0x4c, 0x83, 0xb4, 0x38, 0xba,
0x1c, 0x9f, 0x24, 0x23, 0x65, 0x56, 0x26, 0x08, 0x6d, 0x25, 0x1c, 0xe7, 0x06, 0x56, 0x67, 0x6a,
0x85, 0xf0, 0x2c, 0x31, 0x92, 0x92, 0x5a, 0xa1, 0x02, 0x63, 0xed, 0x81, 0x1d, 0x46, 0xcf, 0xc1,
0xaf, 0x5b, 0xc6, 0x84, 0x5d, 0x09, 0xe3, 0x4d, 0x6c, 0xa6, 0x51, 0x74, 0x4a, 0x18, 0x2f, 0x54,
0x0b, 0x56, 0x62, 0x53, 0x4c, 0xa4, 0xb5, 0xff, 0x3a, 0x34, 0xa9, 0x8c, 0xaf, 0x18, 0x3b, 0xb9,
0x02, 0xc7, 0x40, 0x4d, 0xb8, 0xfe, 0x37, 0xa0, 0x1b, 0x9a, 0x52, 0x51, 0xbd, 0xb5, 0x8e, 0xd8,
0x24, 0x31, 0xa1, 0x5f, 0xf6, 0x17, 0x98, 0x8d, 0x9d, 0x6f, 0xff, 0x4e, 0x94, 0x9d, 0x0f, 0xd3,
0xf0, 0xb1, 0xfa, 0xb4, 0x63, 0x6f, 0xd7, 0x2f, 0xf3, 0xed, 0x1a, 0xe0, 0xbf, 0x0e, 0xed, 0x4c,
0x5f, 0xdc, 0x9a, 0x7f, 0x71, 0x8e, 0x88, 0x9b, 0x1e, 0xeb, 0xeb, 0x16, 0xe7, 0x5f, 0x97, 0x23,
0xfa, 0xf7, 0x61, 0x45, 0x1f, 0x70, 0x9c, 0x90, 0xc6, 0xdb, 0x96, 0x94, 0xec, 0xfb, 0x24, 0x4a,
0x7f, 0x81, 0x39, 0x9b, 0xfc, 0xaf, 0x03, 0xc4, 0xf9, 0x00, 0x94, 0x0a, 0xc8, 0x79, 0x23, 0xce,
0xfe, 0x02, 0x33, 0xd0, 0xfd, 0x77, 0x60, 0x35, 0xb6, 0x87, 0x21, 0x2a, 0x9f, 0xce, 0x19, 0x97,
0xf4, 0x17, 0x98, 0xbb, 0xc9, 0xdf, 0x83, 0x55, 0xa1, 0x83, 0x8d, 0x3a, 0x47, 0xe6, 0xd9, 0x4d,
0xe3, 0x1c, 0xe3, 0x2d, 0x9e, 0xe1, 0x6c, 0xf0, 0xdf, 0x03, 0x7f, 0x50, 0x72, 0x09, 0x95, 0x7f,
0x35, 0x43, 0x65, 0x9f, 0xe9, 0x2f, 0xb0, 0x8a, 0x6d, 0xfe, 0x37, 0xa1, 0x3b, 0x31, 0x5b, 0xab,
0x5e, 0xb7, 0xd4, 0xa6, 0x99, 0x93, 0x06, 0xb4, 0x03, 0x0b, 0xdf, 0xa8, 0x37, 0x9a, 0x58, 0x6f,
0x14, 0xe9, 0xfd, 0x33, 0x0f, 0x36, 0x8d, 0x8e, 0xd4, 0x30, 0xff, 0x59, 0x73, 0x28, 0xa3, 0xb0,
0xbc, 0x5e, 0x1c, 0x79, 0xcd, 0x9a, 0x43, 0x95, 0x9c, 0xcd, 0xfa, 0xc2, 0x27, 0x73, 0xd9, 0x9b,
0xee, 0x24, 0x6a, 0xfe, 0xa6, 0x3c, 0x9f, 0xbd, 0x67, 0x0d, 0xd2, 0x0b, 0x9f, 0xfc, 0x32, 0xe1,
0x2f, 0xf8, 0x51, 0x03, 0x93, 0x8f, 0x7d, 0x1a, 0x55, 0x1a, 0x76, 0xa9, 0xe0, 0x95, 0x4a, 0x85,
0x6d, 0x58, 0xa2, 0x95, 0x14, 0xa3, 0x12, 0xba, 0x09, 0xf2, 0x5f, 0x86, 0x15, 0x2c, 0x0f, 0x8e,
0xc2, 0x31, 0x57, 0x48, 0x32, 0x83, 0x3a, 0xd0, 0xa2, 0x76, 0x6c, 0x54, 0x77, 0x7f, 0x4d, 0xb7,
0x67, 0x2e, 0xfa, 0xb2, 0xd6, 0xbc, 0xbe, 0x6c, 0x71, 0x4e, 0x5f, 0xd6, 0x76, 0xfa, 0x32, 0xab,
0x5f, 0xec, 0xb8, 0xfd, 0xa2, 0xd1, 0xb5, 0xc1, 0x15, 0x5d, 0xdb, 0xd2, 0x75, 0xba, 0xb6, 0xe5,
0x8a, 0xae, 0xad, 0xd4, 0x53, 0x77, 0xaf, 0xd9, 0x53, 0xaf, 0x54, 0xf7, 0xd4, 0x3b, 0xb0, 0x4a,
0x9f, 0x24, 0xef, 0x17, 0xed, 0xcb, 0xaa, 0xc4, 0x74, 0xc0, 0xc1, 0xf7, 0xcb, 0xbe, 0xc1, 0xf8,
0x20, 0x49, 0x87, 0x4f, 0xcb, 0x37, 0x82, 0xff, 0x83, 0xa5, 0xfc, 0xf5, 0xf1, 0x05, 0x65, 0xb6,
0x8b, 0x7c, 0x6e, 0x80, 0x99, 0x8d, 0x56, 0x72, 0x2c, 0x59, 0xcc, 0x58, 0x8f, 0xd1, 0x0e, 0xdc,
0x09, 0xc1, 0x75, 0x3e, 0x17, 0x07, 0xbf, 0xaf, 0xc1, 0xba, 0x35, 0xe0, 0xfc, 0xdf, 0xb2, 0xe8,
0xce, 0x97, 0xb5, 0xe8, 0x8e, 0x61, 0xd1, 0x15, 0xfa, 0xef, 0x54, 0xeb, 0xff, 0x5d, 0x78, 0xc6,
0x12, 0x16, 0xc9, 0x1d, 0x03, 0x5a, 0x8b, 0xe8, 0x76, 0xa7, 0x45, 0x25, 0xc1, 0x32, 0x85, 0x27,
0x03, 0x93, 0xab, 0x3f, 0xe4, 0xa1, 0x5a, 0x7b, 0xa5, 0xe9, 0x97, 0xf5, 0xa3, 0xcb, 0x5f, 0x6b,
0xb0, 0x52, 0x94, 0x34, 0x98, 0x27, 0xd0, 0x1c, 0xb1, 0x31, 0xd7, 0xe6, 0x88, 0xcf, 0x14, 0xf2,
0x13, 0x5d, 0xad, 0x67, 0x09, 0x2a, 0x39, 0xca, 0x53, 0x37, 0xa9, 0xa7, 0xcd, 0x0c, 0x88, 0x61,
0x7b, 0x0d, 0xd3, 0xf6, 0x8c, 0x2a, 0xac, 0x69, 0x55, 0x61, 0x3e, 0x34, 0xb0, 0xdd, 0x57, 0x7a,
0xa1, 0x67, 0xea, 0x1c, 0x64, 0x39, 0x27, 0x3f, 0x53, 0xa9, 0x15, 0x32, 0x24, 0x19, 0xbf, 0x9c,
0x70, 0xd2, 0x47, 0x97, 0x15, 0x00, 0x43, 0xfd, 0x60, 0xa9, 0x9f, 0x7e, 0x03, 0x40, 0xb3, 0x41,
0x59, 0x2a, 0x4d, 0xdd, 0x20, 0x8c, 0x12, 0x9c, 0x3e, 0x70, 0x87, 0x69, 0xa8, 0xb0, 0x36, 0xe5,
0x38, 0xa2, 0x80, 0x60, 0xa0, 0x12, 0xd3, 0xc1, 0x80, 0x0b, 0xd1, 0x7b, 0x96, 0x58, 0xd7, 0xcb,
0xe0, 0x6f, 0x9e, 0x9c, 0xee, 0xd2, 0xf0, 0xe1, 0xde, 0x09, 0x45, 0x8a, 0x99, 0x73, 0x49, 0x73,
0xb2, 0x58, 0x73, 0xfe, 0x7f, 0xb9, 0x6a, 0x2a, 0xf9, 0x32, 0xac, 0x4c, 0x42, 0xcc, 0x53, 0x87,
0xe6, 0x6c, 0x72, 0x99, 0x39, 0xd0, 0x2b, 0x06, 0xe8, 0x2f, 0x41, 0x3d, 0xbb, 0x90, 0xbf, 0x9d,
0x2c, 0xed, 0xfa, 0xca, 0xf2, 0x8e, 0x8b, 0x7f, 0xaf, 0x18, 0xbe, 0x0e, 0xfe, 0xac, 0x7e, 0x0e,
0x30, 0x99, 0xa2, 0x26, 0xe7, 0xba, 0x8c, 0x75, 0x9e, 0x98, 0xb1, 0xce, 0x17, 0x64, 0x6c, 0xad,
0x60, 0xac, 0x23, 0x99, 0x48, 0x64, 0x2b, 0xb5, 0x37, 0x12, 0x47, 0xd1, 0x59, 0x7c, 0x34, 0x1d,
0xeb, 0x8f, 0xaf, 0x66, 0x31, 0x91, 0x77, 0x64, 0x35, 0xf3, 0xf3, 0x09, 0x1f, 0x1a, 0x63, 0x71,
0x26, 0xdb, 0xb4, 0x65, 0x46, 0xbf, 0x11, 0x13, 0xfb, 0x3b, 0xd1, 0x6b, 0x10, 0x50, 0x2e, 0x82,
0xef, 0xc2, 0x73, 0x95, 0x0f, 0x1e, 0x9d, 0x27, 0x8f, 0x9f, 0xe0, 0xd1, 0x8e, 0x7c, 0x34, 0x38,
0xd1, 0xa3, 0x67, 0x7d, 0x3d, 0x69, 0xe4, 0x0d, 0x68, 0x44, 0x45, 0x1b, 0xbb, 0x6d, 0x4d, 0x9e,
0x2b, 0xe8, 0x60, 0x84, 0x2d, 0x7b, 0xa0, 0x49, 0x34, 0xc8, 0xff, 0x60, 0x90, 0xab, 0x80, 0xc1,
0xca, 0x03, 0x1e, 0x0e, 0x79, 0x7a, 0x74, 0x19, 0x0f, 0xf4, 0x90, 0xea, 0xe0, 0x9e, 0x1e, 0x8c,
0x1c, 0xdc, 0x43, 0x4f, 0x38, 0x09, 0x05, 0x3f, 0x18, 0x5e, 0xa8, 0x40, 0xae, 0x97, 0x78, 0x67,
0x72, 0x7a, 0x2a, 0xb8, 0x0e, 0xde, 0x6a, 0x15, 0xfc, 0xcc, 0x83, 0x2e, 0xd2, 0xf3, 0x70, 0xf7,
0xe1, 0xd1, 0xf4, 0xe4, 0x50, 0x9c, 0xa9, 0x72, 0xd2, 0xd3, 0xe5, 0xa4, 0xff, 0x1a, 0xb4, 0x07,
0x6a, 0x78, 0xaa, 0x2a, 0xee, 0x0a, 0xcb, 0xc4, 0x76, 0x41, 0x63, 0xf9, 0x77, 0x61, 0x51, 0x5c,
0xc6, 0x83, 0x43, 0x71, 0xe6, 0x8c, 0xb0, 0x6c, 0xea, 0xfb, 0x0b, 0x4c, 0xe3, 0x15, 0x35, 0xeb,
0x47, 0xb0, 0x72, 0x7f, 0x24, 0xe7, 0x09, 0x6a, 0xec, 0xbe, 0x05, 0xed, 0x48, 0xc8, 0x93, 0x44,
0x55, 0x9b, 0xe5, 0x6b, 0xff, 0x55, 0x68, 0x8d, 0xe4, 0x4e, 0x6d, 0xce, 0x43, 0x4c, 0x21, 0x05,
0x2f, 0x42, 0x67, 0x4f, 0xff, 0xa7, 0x88, 0x36, 0xf9, 0x31, 0xbf, 0x54, 0xc2, 0xc3, 0x9f, 0xbb,
0x6f, 0x41, 0x27, 0xff, 0x62, 0xd1, 0xbf, 0x05, 0xad, 0x03, 0x81, 0x37, 0xf8, 0xdd, 0x3c, 0x05,
0x3c, 0x7a, 0x3f, 0x1a, 0x6d, 0xad, 0xab, 0xe5, 0x81, 0xd8, 0x0f, 0xa7, 0x67, 0xe7, 0xd9, 0x87,
0x93, 0x60, 0xe1, 0xa4, 0x45, 0x9f, 0x29, 0xbe, 0xfe, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x72,
0x9f, 0xea, 0xe9, 0xf3, 0x28, 0x00, 0x00,
0xeb, 0xbf, 0x94, 0x66, 0x31, 0x91, 0x77, 0x64, 0x35, 0xf3, 0xf7, 0x09, 0x1f, 0x1a, 0x63, 0x71,
0x26, 0xdb, 0xb4, 0x65, 0x46, 0xcf, 0x88, 0x89, 0xfd, 0x9d, 0xe8, 0x35, 0x08, 0x28, 0x17, 0xc1,
0x77, 0xe1, 0xb9, 0xca, 0x0b, 0x8f, 0xce, 0x93, 0xc7, 0x4f, 0x70, 0x69, 0x47, 0x5e, 0x1a, 0x9c,
0xe8, 0xd1, 0xb3, 0x3e, 0x9e, 0x34, 0xf2, 0x06, 0x34, 0xa2, 0xa2, 0x8d, 0xdd, 0xb6, 0x26, 0xcf,
0x15, 0x74, 0x30, 0xc2, 0x96, 0x3d, 0xd0, 0x24, 0x1a, 0xe4, 0x1f, 0x18, 0xe4, 0x2a, 0x60, 0xb0,
0xf2, 0x80, 0x87, 0x43, 0x9e, 0x1e, 0x5d, 0xc6, 0x03, 0x3d, 0xa4, 0x3a, 0xb8, 0xa7, 0x07, 0x23,
0x07, 0xf7, 0xd0, 0x13, 0x4e, 0x42, 0xc1, 0x0f, 0x86, 0x17, 0x2a, 0x90, 0xeb, 0x25, 0x9e, 0x99,
0x9c, 0x9e, 0x0a, 0xae, 0x83, 0xb7, 0x5a, 0x05, 0x3f, 0xf3, 0xa0, 0x8b, 0xf4, 0x3c, 0xdc, 0x7d,
0x78, 0x34, 0x3d, 0x39, 0x14, 0x67, 0xaa, 0x9c, 0xf4, 0x74, 0x39, 0xe9, 0xbf, 0x06, 0xed, 0x81,
0x1a, 0x9e, 0xaa, 0x8a, 0xbb, 0xc2, 0x32, 0xb1, 0x5d, 0xd0, 0x58, 0xfe, 0x5d, 0x58, 0x14, 0x97,
0xf1, 0xe0, 0x50, 0x9c, 0x39, 0x23, 0x2c, 0x9b, 0xfa, 0xfe, 0x02, 0xd3, 0x78, 0x45, 0xcd, 0xfa,
0x11, 0xac, 0xdc, 0x1f, 0xc9, 0x79, 0x82, 0x1a, 0xbb, 0x6f, 0x41, 0x3b, 0x12, 0x72, 0x27, 0x51,
0xd5, 0x66, 0xf9, 0xda, 0x7f, 0x15, 0x5a, 0x23, 0xf9, 0xa6, 0x36, 0xe7, 0x22, 0xa6, 0x90, 0x82,
0x17, 0xa1, 0xb3, 0xa7, 0xbf, 0x29, 0xa2, 0x4d, 0x7e, 0xcc, 0x2f, 0x95, 0xf0, 0xf0, 0x71, 0xf7,
0x2d, 0xe8, 0xe4, 0x3f, 0x40, 0xfa, 0xb7, 0xa0, 0x75, 0x20, 0xf0, 0x04, 0xbf, 0x9b, 0xa7, 0x80,
0x47, 0xef, 0x47, 0xa3, 0xad, 0x75, 0xb5, 0x3c, 0x10, 0xfb, 0xe1, 0xf4, 0xec, 0x3c, 0xfb, 0x70,
0x12, 0x2c, 0x9c, 0xb4, 0xe8, 0xaf, 0xc7, 0xd7, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x0e,
0x12, 0x6b, 0x42, 0x29, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
......
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