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) { ...@@ -75,6 +75,7 @@ func getNodes(db dbm.KV, key []byte) (map[string]struct{}, []string, error) {
return nodesMap, nodesArray, nil return nodesMap, nodesArray, nil
} }
// manager 合约 分叉前写入 现在不用了
func getConfigManageNodes(db dbm.KV, title string) (map[string]struct{}, []string, error) { func getConfigManageNodes(db dbm.KV, title string) (map[string]struct{}, []string, error) {
key := calcManageConfigNodesKey(title) key := calcManageConfigNodesKey(title)
return getNodes(db, key) return getNodes(db, key)
...@@ -275,8 +276,7 @@ func getConfigNodes(db dbm.KV, title string) (map[string]struct{}, []string, []b ...@@ -275,8 +276,7 @@ func getConfigNodes(db dbm.KV, title string) (map[string]struct{}, []string, []b
if errors.Cause(err) != pt.ErrTitleNotExist { if errors.Cause(err) != pt.ErrTitleNotExist {
return nil, nil, nil, errors.Wrapf(err, "getNodes para for title:%s", title) return nil, nil, nil, errors.Wrapf(err, "getNodes para for title:%s", title)
} }
key = calcManageConfigNodesKey(title) nodes, nodesArray, err = getConfigManageNodes(db, title)
nodes, nodesArray, err = getNodes(db, key)
if err != nil { if err != nil {
return nil, nil, nil, errors.Wrapf(err, "getNodes manager for title:%s", title) 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 ...@@ -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) { func (a *action) getNodesGroup(title string) (map[string]struct{}, []string, error) {
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
// 如果高度是分叉前,获取老的Nodes
if a.exec.GetMainHeight() < pt.GetDappForkHeight(cfg, pt.ForkCommitTx) { if a.exec.GetMainHeight() < pt.GetDappForkHeight(cfg, pt.ForkCommitTx) {
nodes, nodesArray, err := getConfigManageNodes(a.db, title) nodes, nodesArray, err := getConfigManageNodes(a.db, title)
if err != nil { if err != nil {
...@@ -297,7 +298,18 @@ func (a *action) getNodesGroup(title string) (map[string]struct{}, []string, err ...@@ -297,7 +298,18 @@ func (a *action) getNodesGroup(title string) (map[string]struct{}, []string, err
nodes, nodesArray, _, err := getConfigNodes(a.db, title) nodes, nodesArray, _, err := getConfigNodes(a.db, title)
return nodes, nodesArray, err 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 { func (a *action) isValidSuperNode(addr string) error {
...@@ -324,7 +336,6 @@ func updateCommitBlockHashs(stat *pt.ParacrossHeightStatus, commit *pt.Paracross ...@@ -324,7 +336,6 @@ func updateCommitBlockHashs(stat *pt.ParacrossHeightStatus, commit *pt.Paracross
} }
stat.BlockDetails.BlockHashs = append(stat.BlockDetails.BlockHashs, commit.BlockHash) stat.BlockDetails.BlockHashs = append(stat.BlockDetails.BlockHashs, commit.BlockHash)
stat.BlockDetails.TxResults = append(stat.BlockDetails.TxResults, commit.TxResult) stat.BlockDetails.TxResults = append(stat.BlockDetails.TxResults, commit.TxResult)
} }
//根据nodes过滤掉可能退出了的addrs //根据nodes过滤掉可能退出了的addrs
...@@ -337,7 +348,17 @@ func updateCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{} ...@@ -337,7 +348,17 @@ func updateCommitAddrs(stat *pt.ParacrossHeightStatus, nodes map[string]struct{}
} }
} }
stat.Details = details 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 //自共识分阶段使能,综合考虑挖矿奖励和共识分配奖励,判断是否自共识使能需要采用共识的高度,而不能采用当前区块高度a.height
...@@ -511,14 +532,32 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error ...@@ -511,14 +532,32 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error
} }
validAddrs := getValidAddrs(nodesMap, commitAddrs) 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() cfg := a.api.GetConfig()
err := a.preCheckCommitInfo(commit, commitAddrs) err := a.preCheckCommitInfo(commit, commitAddrs)
...@@ -534,7 +573,8 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -534,7 +573,8 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
// 在完成共识之后来的, 增加 record log, 只记录不修改已经达成的共识 // 在完成共识之后来的, 增加 record log, 只记录不修改已经达成的共识
if commit.Height <= titleStatus.Height { if commit.Height <= titleStatus.Height {
clog.Debug("paracross.Commit record", "node", commitAddrs, "titile", commit.Title, "height", commit.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 ...@@ -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, //用commit.MainBlockHeight 判断更准确,如果用a.exec.MainHeight也可以,但是可能收到MainHeight之前的高度共识tx,
// 后面loopCommitTxDone时候也是用当前共识高度大于分叉高度判断 // 后面loopCommitTxDone时候也是用当前共识高度大于分叉高度判断
if pt.IsParaForkHeight(cfg, commit.MainBlockHeight, pt.ForkLoopCheckCommitTxDone) { if pt.IsParaForkHeight(cfg, commit.MainBlockHeight, pt.ForkLoopCheckCommitTxDone) {
...@@ -582,8 +633,9 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -582,8 +633,9 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
//平行链fork pt.ForkCommitTx=0,主链在ForkCommitTx后支持nodegroup,这里平行链dappFork一定为true //平行链fork pt.ForkCommitTx=0,主链在ForkCommitTx后支持nodegroup,这里平行链dappFork一定为true
if cfg.IsDappFork(commit.MainBlockHeight, pt.ParaX, pt.ForkCommitTx) { if cfg.IsDappFork(commit.MainBlockHeight, pt.ParaX, pt.ForkCommitTx) {
updateCommitAddrs(stat, nodes) 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更新而更新 //fork之前记录的stat 没有根据nodes更新而更新
if pt.IsParaForkHeight(cfg, stat.MainHeight, pt.ForkLoopCheckCommitTxDone) { if pt.IsParaForkHeight(cfg, stat.MainHeight, pt.ForkLoopCheckCommitTxDone) {
r := makeCommitStatReceipt(stat) r := makeCommitStatReceipt(stat)
...@@ -591,7 +643,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -591,7 +643,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
} }
if commit.Height > titleStatus.Height+1 { 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开始 //平行链由主链共识无缝切换,即接收第一个收到的高度,可以不从0开始
allow, err := a.isAllowConsensJump(commit, titleStatus) allow, err := a.isAllowConsensJump(commit, titleStatus)
if err != nil { if err != nil {
...@@ -602,7 +654,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -602,7 +654,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
return receipt, nil return receipt, nil
} }
} }
r, err := a.commitTxDone(commit, stat, titleStatus, nodes) r, err := a.commitTxDone(commit, stat, titleStatus, nodes, supervisionNodes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -612,7 +664,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -612,7 +664,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
//分叉以前stat里面只记录了blockhash的信息,没有crossTxHash等信息,无法通过stat直接重构出mostCommitStatus //分叉以前stat里面只记录了blockhash的信息,没有crossTxHash等信息,无法通过stat直接重构出mostCommitStatus
func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.ParacrossHeightStatus, titleStatus *pt.ParacrossStatus, 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{} receipt := &types.Receipt{}
clog.Debug("paracross.Commit commit", "stat.title", stat.Title, "stat.height", stat.Height, "notes", len(nodes)) 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 ...@@ -624,9 +676,23 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac
if !isCommitDone(len(nodes), mostCount) { if !isCommitDone(len(nodes), mostCount) {
return receipt, nil 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))) clog.Debug("paracross.Commit commit ----pass", "most", mostCount, "mostHash", common.ToHex([]byte(mostHash)))
stat.Status = pt.ParacrossStatusCommitDone stat.Status = pt.ParacrossStatusCommitDone
saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat) _ = saveTitleHeight(a.db, calcTitleHeightKey(stat.Title, stat.Height), stat)
//之前记录的stat 状态没更新 //之前记录的stat 状态没更新
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
...@@ -658,7 +724,7 @@ func (a *action) commitTxDoneStep2(nodeStatus *pt.ParacrossNodeStatus, stat *pt. ...@@ -658,7 +724,7 @@ func (a *action) commitTxDoneStep2(nodeStatus *pt.ParacrossNodeStatus, stat *pt.
titleStatus.MainHeight = nodeStatus.MainBlockHeight titleStatus.MainHeight = nodeStatus.MainBlockHeight
titleStatus.MainHash = nodeStatus.MainBlockHash 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)) clog.Debug("paracross.Commit commit done", "height", nodeStatus.Height, "statusBlockHash", common.ToHex(nodeStatus.BlockHash))
...@@ -1345,29 +1411,6 @@ func (a *action) isAllowTransfer() error { ...@@ -1345,29 +1411,6 @@ func (a *action) isAllowTransfer() error {
return nil 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) { func (a *action) Transfer(transfer *types.AssetsTransfer, tx *types.Transaction, index int) (*types.Receipt, error) {
clog.Debug("Paracross.Exec Transfer", "symbol", transfer.Cointoken, "amount", clog.Debug("Paracross.Exec Transfer", "symbol", transfer.Cointoken, "amount",
transfer.Amount, "to", tx.To) transfer.Amount, "to", tx.To)
...@@ -1418,3 +1461,8 @@ func (a *action) TransferToExec(transfer *types.AssetsTransferToExec, tx *types. ...@@ -1418,3 +1461,8 @@ func (a *action) TransferToExec(transfer *types.AssetsTransferToExec, tx *types.
} }
return nil, types.ErrToAddrNotSameToExecAddr 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 ...@@ -125,3 +125,9 @@ func (e *Paracross) Exec_ParaBindMiner(payload *pt.ParaBindMinerCmd, tx *types.T
a := newAction(e, tx) a := newAction(e, tx)
return a.bindMiner(payload) 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 ( ...@@ -38,6 +38,14 @@ var (
paraBindMinderAddr string paraBindMinderAddr string
paraBindMinderNode string paraBindMinderNode string
//监督节点
paraSupervisionNodes string
paraSupervisionNodeAddr string
paraSupervisionNodeGroupStatusAddrs string
paraSupervisionNodeGroupIDPrefix string
paraSupervisionSelfConsensStages string
paraSupervisionSelfConsensStageIDPrefix string
) )
func setPrefix() { func setPrefix() {
...@@ -67,6 +75,12 @@ func setPrefix() { ...@@ -67,6 +75,12 @@ func setPrefix() {
localNodeGroupStatusTitle = "LODB-paracross-nodegroupStatusTitle-" 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 { func calcTitleKey(t string) []byte {
...@@ -195,3 +209,19 @@ func calcParaBindMinerAddr(node, bind string) []byte { ...@@ -195,3 +209,19 @@ func calcParaBindMinerAddr(node, bind string) []byte {
func calcParaBindMinerNode(node string) []byte { func calcParaBindMinerNode(node string) []byte {
return []byte(paraBindMinderNode + node) 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 ( ...@@ -26,21 +26,14 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
// 构造一个4个节点的平行链数据, 进行测试
const (
SignedType = types.SECP256K1
)
var ( var (
MainBlockHash10 = []byte("main block hash 10") MainBlockHash10 = []byte("main block hash 10")
MainBlockHeight = int64(10) MainBlockHeight = int64(10)
CurHeight = int64(10) CurHeight = int64(10)
Title = string("user.p.test.") Title = "user.p.test."
TitleHeight = int64(10) TitleHeight = int64(10)
PerBlock = []byte("block-hash-9") PerBlock = []byte("block-hash-9")
CurBlock = []byte("block-hash-10") CurBlock = []byte("block-hash-10")
PerState = []byte("state-hash-9")
CurState = []byte("state-hash-10")
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4 PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
...@@ -53,8 +46,6 @@ var ( ...@@ -53,8 +46,6 @@ var (
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"), []byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
} }
TokenSymbol = "X"
MainBlockHeightForTransfer = int64(9)
chain33TestCfg = types.NewChain33Config(testnode.DefaultConfig) chain33TestCfg = types.NewChain33Config(testnode.DefaultConfig)
chain33TestMainCfg = types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"test\"", 1)) chain33TestMainCfg = types.NewChain33Config(strings.Replace(types.GetDefaultCfgstring(), "Title=\"local\"", "Title=\"test\"", 1))
) )
...@@ -92,7 +83,6 @@ func init() { ...@@ -92,7 +83,6 @@ func init() {
} }
func (suite *CommitTestSuite) SetupSuite() { func (suite *CommitTestSuite) SetupSuite() {
suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024) suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024)
// memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb // memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb
//suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024) //suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024)
...@@ -118,7 +108,7 @@ func (suite *CommitTestSuite) SetupSuite() { ...@@ -118,7 +108,7 @@ func (suite *CommitTestSuite) SetupSuite() {
// setup title nodes : len = 4 // setup title nodes : len = 4
nodeConfigKey := calcManageConfigNodesKey(Title) nodeConfigKey := calcManageConfigNodesKey(Title)
nodeValue := makeNodeInfo(Title, Title, 4) 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) value, err := suite.stateDB.Get(nodeConfigKey)
if err != nil { if err != nil {
suite.T().Error("get setup title failed", err) suite.T().Error("get setup title failed", err)
...@@ -129,7 +119,7 @@ func (suite *CommitTestSuite) SetupSuite() { ...@@ -129,7 +119,7 @@ func (suite *CommitTestSuite) SetupSuite() {
stageKey := calcParaSelfConsStagesKey() stageKey := calcParaSelfConsStagesKey()
stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes} stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes}
stages := &pt.SelfConsensStages{Items: []*pt.SelfConsensStage{stage}} 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) value, err = suite.stateDB.Get(stageKey)
if err != nil { if err != nil {
suite.T().Error("get setup stages failed", err) suite.T().Error("get setup stages failed", err)
...@@ -142,7 +132,7 @@ func (suite *CommitTestSuite) SetupSuite() { ...@@ -142,7 +132,7 @@ func (suite *CommitTestSuite) SetupSuite() {
titleStatus.Title = Title titleStatus.Title = Title
titleStatus.Height = CurHeight - 1 titleStatus.Height = CurHeight - 1
titleStatus.BlockHash = PerBlock titleStatus.BlockHash = PerBlock
saveTitle(suite.stateDB, calcTitleKey(Title), &titleStatus) _ = saveTitle(suite.stateDB, calcTitleKey(Title), &titleStatus)
// setup api // setup api
hashes := &types.ReqHashes{Hashes: [][]byte{MainBlockHash10}} hashes := &types.ReqHashes{Hashes: [][]byte{MainBlockHash10}}
...@@ -200,13 +190,13 @@ func signTx(s suite.Suite, tx *types.Transaction, hexPrivKey string) (*types.Tra ...@@ -200,13 +190,13 @@ func signTx(s suite.Suite, tx *types.Transaction, hexPrivKey string) (*types.Tra
return tx, err return tx, err
} }
bytes, err := common.FromHex(hexPrivKey[:]) bytesData, err := common.FromHex(hexPrivKey[:])
if err != nil { if err != nil {
s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err) s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err)
return tx, err return tx, err
} }
privKey, err := c.PrivKeyFromBytes(bytes) privKey, err := c.PrivKeyFromBytes(bytesData)
if err != nil { if err != nil {
s.T().Error("TestExec", "PrivKeyFromBytes failed", err) s.T().Error("TestExec", "PrivKeyFromBytes failed", err)
return tx, err return tx, err
...@@ -224,13 +214,13 @@ func getPrivKey(s suite.Suite, hexPrivKey string) (crypto.PrivKey, error) { ...@@ -224,13 +214,13 @@ func getPrivKey(s suite.Suite, hexPrivKey string) (crypto.PrivKey, error) {
return nil, err return nil, err
} }
bytes, err := common.FromHex(hexPrivKey[:]) bytesData, err := common.FromHex(hexPrivKey[:])
if err != nil { if err != nil {
s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err) s.T().Error("TestExec", "Hex2Bytes privkey faiiled", err)
return nil, err return nil, err
} }
privKey, err := c.PrivKeyFromBytes(bytes) privKey, err := c.PrivKeyFromBytes(bytesData)
if err != nil { if err != nil {
s.T().Error("TestExec", "PrivKeyFromBytes failed", err) s.T().Error("TestExec", "PrivKeyFromBytes failed", err)
return nil, err return nil, err
...@@ -311,6 +301,7 @@ func checkDoneReceipt(suite suite.Suite, receipt *types.Receipt, commitCnt int) ...@@ -311,6 +301,7 @@ func checkDoneReceipt(suite suite.Suite, receipt *types.Receipt, commitCnt int)
} }
func checkRecordReceipt(suite *CommitTestSuite, 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.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
assert.Len(suite.T(), receipt.KV, 0) assert.Len(suite.T(), receipt.KV, 0)
assert.Len(suite.T(), receipt.Logs, 1) assert.Len(suite.T(), receipt.Logs, 1)
...@@ -363,105 +354,6 @@ func TestGetTitle(t *testing.T) { ...@@ -363,105 +354,6 @@ func TestGetTitle(t *testing.T) {
assert.Equal(t, titleExpect, title) 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 { type VoteTestSuite struct {
suite.Suite suite.Suite
stateDB dbm.KV stateDB dbm.KV
...@@ -489,7 +381,7 @@ func (s *VoteTestSuite) SetupSuite() { ...@@ -489,7 +381,7 @@ func (s *VoteTestSuite) SetupSuite() {
stageKey := calcParaSelfConsStagesKey() stageKey := calcParaSelfConsStagesKey()
stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes} stage := &pt.SelfConsensStage{StartHeight: 0, Enable: pt.ParaConfigYes}
stages := &pt.SelfConsensStages{Items: []*pt.SelfConsensStage{stage}} 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) value, err := s.stateDB.Get(stageKey)
if err != nil { if err != nil {
s.T().Error("get setup stages failed", err) s.T().Error("get setup stages failed", err)
...@@ -565,7 +457,7 @@ func (s *VoteTestSuite) TestVoteTx() { ...@@ -565,7 +457,7 @@ func (s *VoteTestSuite) TestVoteTx() {
//s.T().Log(string(kv.GetKey())) //s.T().Log(string(kv.GetKey()))
if bytes.Equal(key, kv.Key) { if bytes.Equal(key, kv.Key) {
var rst pt.ParacrossNodeStatus var rst pt.ParacrossNodeStatus
types.Decode(kv.GetValue(), &rst) _ = types.Decode(kv.GetValue(), &rst)
s.Equal([]byte{0x4d}, rst.TxResult) s.Equal([]byte{0x4d}, rst.TxResult)
s.Equal([]byte{0x25}, rst.CrossTxResult) s.Equal([]byte{0x25}, rst.CrossTxResult)
s.Equal(7, len(rst.TxHashs)) s.Equal(7, len(rst.TxHashs))
...@@ -662,7 +554,7 @@ func (s *VoteTestSuite) TestVoteTxFork() { ...@@ -662,7 +554,7 @@ func (s *VoteTestSuite) TestVoteTxFork() {
//s.T().Log(string(kv.GetKey())) //s.T().Log(string(kv.GetKey()))
if bytes.Equal(key, kv.Key) { if bytes.Equal(key, kv.Key) {
var rst pt.ParacrossNodeStatus var rst pt.ParacrossNodeStatus
types.Decode(kv.GetValue(), &rst) _ = types.Decode(kv.GetValue(), &rst)
s.Equal([]byte("8e"), rst.TxResult) s.Equal([]byte("8e"), rst.TxResult)
s.Equal([]byte("22"), rst.CrossTxResult) s.Equal([]byte("22"), rst.CrossTxResult)
s.Equal(1, len(rst.TxHashs)) s.Equal(1, len(rst.TxHashs))
...@@ -739,7 +631,7 @@ func createTxsGroup(s suite.Suite, txs []*types.Transaction) ([]*types.Transacti ...@@ -739,7 +631,7 @@ func createTxsGroup(s suite.Suite, txs []*types.Transaction) ([]*types.Transacti
} }
privKey, _ := getPrivKey(s, PrivKeyA) privKey, _ := getPrivKey(s, PrivKeyA)
for i := range group.Txs { for i := range group.Txs {
group.SignN(i, int32(types.SECP256K1), privKey) _ = group.SignN(i, int32(types.SECP256K1), privKey)
} }
return group.Txs, nil return group.Txs, nil
} }
...@@ -794,11 +686,11 @@ func TestUpdateCommitBlockHashs(t *testing.T) { ...@@ -794,11 +686,11 @@ func TestUpdateCommitBlockHashs(t *testing.T) {
} }
updateCommitBlockHashs(stat, commit) 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]) assert.Equal(t, commit.BlockHash, stat.BlockDetails.BlockHashs[0])
updateCommitBlockHashs(stat, commit) 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]) assert.Equal(t, commit.BlockHash, stat.BlockDetails.BlockHashs[0])
commit2 := &pt.ParacrossNodeStatus{ commit2 := &pt.ParacrossNodeStatus{
...@@ -812,7 +704,7 @@ func TestUpdateCommitBlockHashs(t *testing.T) { ...@@ -812,7 +704,7 @@ func TestUpdateCommitBlockHashs(t *testing.T) {
CrossTxHashs: [][]byte{[]byte("hash2")}, CrossTxHashs: [][]byte{[]byte("hash2")},
} }
updateCommitBlockHashs(stat, commit2) 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]) assert.Equal(t, commit2.BlockHash, stat.BlockDetails.BlockHashs[1])
} }
......
...@@ -280,7 +280,6 @@ func (a *action) nodeJoin(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -280,7 +280,6 @@ func (a *action) nodeJoin(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
receipt.KV = append(receipt.KV, r.KV...) receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...) receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil return receipt, nil
} }
func (a *action) nodeQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) { func (a *action) nodeQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
...@@ -309,7 +308,6 @@ 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{}, Votes: &pt.ParaNodeVoteDetail{},
Height: a.height} Height: a.height}
return makeNodeConfigReceipt(a.fromaddr, config, nil, stat), nil return makeNodeConfigReceipt(a.fromaddr, config, nil, stat), nil
} }
func (a *action) nodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) { func (a *action) nodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
...@@ -357,7 +355,6 @@ func (a *action) nodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, erro ...@@ -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) 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) { func (a *action) nodeModify(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
...@@ -602,7 +599,6 @@ func (a *action) nodeVote(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.Status = pt.ParaApplyClosed
stat.Height = a.height stat.Height = a.height
} }
} else { } else {
if stat.Status == pt.ParaApplyJoining { if stat.Status == pt.ParaApplyJoining {
r, err := unpdateNodeGroup(a.db, config.Title, stat.TargetAddr, true) r, err := unpdateNodeGroup(a.db, config.Title, stat.TargetAddr, true)
...@@ -651,7 +647,6 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -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) receiptDone := makeVoteDoneReceipt(stat, len(nodes), len(stat.Votes.Addrs), most, pt.ParaNodeVoteStr[vote], stat.Status)
receipt = mergeReceipt(receipt, receiptDone) receipt = mergeReceipt(receipt, receiptDone)
return receipt, nil return receipt, nil
} }
func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) { 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 * ...@@ -929,7 +924,6 @@ func (a *action) nodeGroupApproveModify(config *pt.ParaNodeGroupConfig, modify *
receipt.Logs = append(receipt.Logs, r.Logs...) receipt.Logs = append(receipt.Logs, r.Logs...)
return receipt, nil return receipt, nil
} }
func (a *action) nodeGroupApproveApply(config *pt.ParaNodeGroupConfig, apply *pt.ParaNodeGroupStatus) (*types.Receipt, error) { 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 ...@@ -971,7 +965,6 @@ func (a *action) nodeGroupApproveApply(config *pt.ParaNodeGroupConfig, apply *pt
} }
return receipt, nil return receipt, nil
} }
// NodeGroupApprove super addr approve the node group apply // NodeGroupApprove super addr approve the node group apply
...@@ -1068,13 +1061,11 @@ func (a *action) NodeGroupConfig(config *pt.ParaNodeGroupConfig) (*types.Receipt ...@@ -1068,13 +1061,11 @@ func (a *action) NodeGroupConfig(config *pt.ParaNodeGroupConfig) (*types.Receipt
return nil, err return nil, err
} }
return a.nodeGroupApply(config) return a.nodeGroupApply(config)
} else if config.Op == pt.ParacrossNodeGroupApprove { } else if config.Op == pt.ParacrossNodeGroupApprove {
if config.Id == "" { if config.Id == "" {
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
} }
return a.nodeGroupApprove(config) return a.nodeGroupApprove(config)
} else if config.Op == pt.ParacrossNodeGroupQuit { } else if config.Op == pt.ParacrossNodeGroupQuit {
if config.Id == "" { if config.Id == "" {
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
...@@ -1122,5 +1113,4 @@ func (a *action) NodeConfig(config *pt.ParaNodeAddrConfig) (*types.Receipt, erro ...@@ -1122,5 +1113,4 @@ func (a *action) NodeConfig(config *pt.ParaNodeAddrConfig) (*types.Receipt, erro
default: default:
return nil, pt.ErrParaUnSupportNodeOper return nil, pt.ErrParaUnSupportNodeOper
} }
} }
...@@ -25,7 +25,7 @@ import ( ...@@ -25,7 +25,7 @@ import (
var ( var (
PrivKey14K = "CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944" // 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt PrivKey14K = "CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944" // 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
Account14K = "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt" Account14K = "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
Account1MC = "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs" //Account1MC = "1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
applyAddrs = "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4, 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR, 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k,1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs" applyAddrs = "1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4, 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR, 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k,1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"
Account12Q = "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv" Account12Q = "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
...@@ -60,7 +60,6 @@ func createRawNodeGroupApplyTx(apply *pt.ParaNodeGroupConfig) (*types.Transactio ...@@ -60,7 +60,6 @@ func createRawNodeGroupApplyTx(apply *pt.ParaNodeGroupConfig) (*types.Transactio
} }
return tx, nil return tx, nil
} }
type NodeManageTestSuite struct { type NodeManageTestSuite struct {
...@@ -68,13 +67,10 @@ type NodeManageTestSuite struct { ...@@ -68,13 +67,10 @@ type NodeManageTestSuite struct {
stateDB dbm.KV stateDB dbm.KV
localDB *dbmock.KVDB localDB *dbmock.KVDB
api *apimock.QueueProtocolAPI api *apimock.QueueProtocolAPI
exec *Paracross exec *Paracross
//title string
} }
func (suite *NodeManageTestSuite) SetupSuite() { func (suite *NodeManageTestSuite) SetupSuite() {
suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024) suite.stateDB, _ = dbm.NewGoMemDB("state", "state", 1024)
// memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb // memdb 不支持KVDB接口, 等测试完Exec , 再扩展 memdb
//suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024) //suite.localDB, _ = dbm.NewGoMemDB("local", "local", 1024)
...@@ -90,12 +86,6 @@ func (suite *NodeManageTestSuite) SetupSuite() { ...@@ -90,12 +86,6 @@ func (suite *NodeManageTestSuite) SetupSuite() {
suite.exec.SetBlockInfo([]byte(""), []byte(""), 3) suite.exec.SetBlockInfo([]byte(""), []byte(""), 3)
enableParacrossTransfer = false 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.consensus.sub.para.MainForkParacrossCommitTx", int64(1))
chain33TestCfg.S("config.exec.sub.manage.superManager", []interface{}{Account12Q}) chain33TestCfg.S("config.exec.sub.manage.superManager", []interface{}{Account12Q})
...@@ -105,7 +95,6 @@ func (suite *NodeManageTestSuite) SetupSuite() { ...@@ -105,7 +95,6 @@ func (suite *NodeManageTestSuite) SetupSuite() {
Block: &types.Block{}, Block: &types.Block{},
} }
MainBlockHash10 = blockDetail.Block.Hash(chain33TestCfg) MainBlockHash10 = blockDetail.Block.Hash(chain33TestCfg)
} }
func (suite *NodeManageTestSuite) TestSetup() { func (suite *NodeManageTestSuite) TestSetup() {
...@@ -145,7 +134,6 @@ func checkGroupApplyReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) ...@@ -145,7 +134,6 @@ func checkGroupApplyReceipt(suite *NodeManageTestSuite, receipt *types.Receipt)
assert.Len(suite.T(), receipt.Logs, 1) assert.Len(suite.T(), receipt.Logs, 1)
assert.Equal(suite.T(), int32(pt.TyLogParaNodeGroupConfig), receipt.Logs[0].Ty) assert.Equal(suite.T(), int32(pt.TyLogParaNodeGroupConfig), receipt.Logs[0].Ty)
} }
func checkGroupApproveReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) { func checkGroupApproveReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
...@@ -164,7 +152,6 @@ func checkJoinReceipt(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.TyLogParaNodeConfig), receipt.Logs[0].Ty)
assert.Equal(suite.T(), int32(pt.ParaApplyJoining), stat.Status) assert.Equal(suite.T(), int32(pt.ParaApplyJoining), stat.Status)
assert.NotNil(suite.T(), stat.Votes) assert.NotNil(suite.T(), stat.Votes)
} }
func checkQuitReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) { func checkQuitReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
...@@ -179,7 +166,6 @@ 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.TyLogParaNodeConfig), receipt.Logs[0].Ty)
assert.Equal(suite.T(), int32(pt.ParaApplyQuiting), stat.Status) assert.Equal(suite.T(), int32(pt.ParaApplyQuiting), stat.Status)
assert.NotNil(suite.T(), stat.Votes) assert.NotNil(suite.T(), stat.Votes)
} }
func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int) { func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int) {
...@@ -189,15 +175,14 @@ func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count ...@@ -189,15 +175,14 @@ func checkVoteReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count
err := types.Decode(receipt.KV[0].Value, &stat) err := types.Decode(receipt.KV[0].Value, &stat)
assert.Nil(suite.T(), err, "decode ParaNodeAddrStatus failed") assert.Nil(suite.T(), err, "decode ParaNodeAddrStatus failed")
assert.Len(suite.T(), stat.Votes.Votes, count) assert.Len(suite.T(), stat.Votes.Votes, count)
} }
func checkVoteDoneReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int, join bool) { func checkVoteDoneReceipt(suite *NodeManageTestSuite, receipt *types.Receipt, count int, join bool) {
_ = count
suite.NotNil(receipt) suite.NotNil(receipt)
assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk)) assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
suite.T().Log("checkVoteDoneReceipt", "kvlen", len(receipt.KV)) suite.T().Log("checkVoteDoneReceipt", "kvlen", len(receipt.KV))
_, arry, err := getParacrossNodes(suite.stateDB, Title) _, arry, err := getParacrossNodes(suite.stateDB, Title)
suite.Suite.Nil(err) suite.Suite.Nil(err)
if join { if join {
...@@ -264,7 +249,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfigQuit() { ...@@ -264,7 +249,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfigQuit() {
nodeCommit(suite, PrivKeyB, tx) nodeCommit(suite, PrivKeyB, tx)
//checkGroupApproveReceipt(suite, receipt) //checkGroupApproveReceipt(suite, receipt)
} }
func (suite *NodeManageTestSuite) testNodeGroupConfig() { func (suite *NodeManageTestSuite) testNodeGroupConfig() {
...@@ -296,7 +280,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfig() { ...@@ -296,7 +280,6 @@ func (suite *NodeManageTestSuite) testNodeGroupConfig() {
receipt = nodeCommit(suite, PrivKey12Q, tx) receipt = nodeCommit(suite, PrivKey12Q, tx)
checkGroupApproveReceipt(suite, receipt) checkGroupApproveReceipt(suite, receipt)
} }
func (suite *NodeManageTestSuite) testNodeConfig() { func (suite *NodeManageTestSuite) testNodeConfig() {
...@@ -342,7 +325,6 @@ func (suite *NodeManageTestSuite) testNodeConfig() { ...@@ -342,7 +325,6 @@ func (suite *NodeManageTestSuite) testNodeConfig() {
func (suite *NodeManageTestSuite) TestExec() { func (suite *NodeManageTestSuite) TestExec() {
suite.testNodeGroupConfig() suite.testNodeGroupConfig()
suite.testNodeConfig() suite.testNodeConfig()
} }
func TestNodeManageSuite(t *testing.T) { func TestNodeManageSuite(t *testing.T) {
...@@ -350,7 +332,6 @@ func TestNodeManageSuite(t *testing.T) { ...@@ -350,7 +332,6 @@ func TestNodeManageSuite(t *testing.T) {
} }
func (suite *NodeManageTestSuite) TearDownSuite() { func (suite *NodeManageTestSuite) TearDownSuite() {
} }
func TestGetAddrGroup(t *testing.T) { func TestGetAddrGroup(t *testing.T) {
...@@ -374,7 +355,6 @@ func TestGetAddrGroup(t *testing.T) { ...@@ -374,7 +355,6 @@ func TestGetAddrGroup(t *testing.T) {
ret = getConfigAddrs(addrs) ret = getConfigAddrs(addrs)
assert.Equal(t, []string(nil), ret) assert.Equal(t, []string(nil), ret)
assert.Equal(t, 0, len(ret)) assert.Equal(t, 0, len(ret))
} }
func TestUpdateVotes(t *testing.T) { func TestUpdateVotes(t *testing.T) {
...@@ -402,5 +382,4 @@ func TestGetNodeIdSuffix(t *testing.T) { ...@@ -402,5 +382,4 @@ func TestGetNodeIdSuffix(t *testing.T) {
id = "mavl-paracross-title-nodegroupid-user.p.para.-0xb6cd0274aa5f839fa2291ecfbfc626b494aacac7587a61e444e9f848a4c02d7b-1" id = "mavl-paracross-title-nodegroupid-user.p.para.-0xb6cd0274aa5f839fa2291ecfbfc626b494aacac7587a61e444e9f848a4c02d7b-1"
rtID = getParaNodeIDSuffix(id) rtID = getParaNodeIDSuffix(id)
assert.Equal(t, txID, rtID) 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
...@@ -24,9 +24,10 @@ message ParacrossHeightStatus { ...@@ -24,9 +24,10 @@ message ParacrossHeightStatus {
string title = 2; string title = 2;
int64 height = 3; int64 height = 3;
ParacrossStatusDetails details = 4; ParacrossStatusDetails details = 4;
int64 mainHeight = 5; ParacrossStatusDetails supervisionDetails = 5;
bytes mainHash = 6; int64 mainHeight = 6;
ParacrossStatusBlockDetails blockDetails = 7; bytes mainHash = 7;
ParacrossStatusBlockDetails blockDetails = 8;
} }
message ParacrossHeightStatusRsp { message ParacrossHeightStatusRsp {
......
...@@ -59,4 +59,10 @@ var ( ...@@ -59,4 +59,10 @@ var (
ErrConsensClosed = errors.New("ErrConsensClosed") ErrConsensClosed = errors.New("ErrConsensClosed")
//ErrBlsSignVerify bls12-381 aggregate sign verify //ErrBlsSignVerify bls12-381 aggregate sign verify
ErrBlsSignVerify = errors.New("ErrBlsSignVerify") 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 ( ...@@ -49,6 +49,12 @@ const (
TyLogParaCrossAssetTransfer = 670 TyLogParaCrossAssetTransfer = 670
TyLogParaBindMinerAddr = 671 TyLogParaBindMinerAddr = 671
TyLogParaBindMinerNode = 672 TyLogParaBindMinerNode = 672
TyLogParaSupervisionNodeGroupConfig = 680
TyLogParaSupervisionNodeGroupAddrsUpdate
TyLogParaSupervisionNodeConfig
TyLogParaSupervisionNodeStatusUpdate
TyLogParaStageSupervisionGroupUpdate
) )
// action type // action type
...@@ -164,6 +170,12 @@ const ( ...@@ -164,6 +170,12 @@ const (
ParacrossNodeGroupModify ParacrossNodeGroupModify
) )
const (
ParacrossSupervisionNodeApply = iota + 1
ParacrossSupervisionNodeApprove
ParacrossSupervisionNodeQuit
)
var ( var (
// ParacrossActionCommitStr Commit string // ParacrossActionCommitStr Commit string
ParacrossActionCommitStr = string("Commit") ParacrossActionCommitStr = string("Commit")
......
...@@ -6,13 +6,12 @@ package types ...@@ -6,13 +6,12 @@ package types
import ( import (
context "context" context "context"
fmt "fmt" fmt "fmt"
math "math"
types "github.com/33cn/chain33/types" types "github.com/33cn/chain33/types"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
math "math"
) )
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
...@@ -128,9 +127,10 @@ type ParacrossHeightStatus struct { ...@@ -128,9 +127,10 @@ type ParacrossHeightStatus struct {
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,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"` Details *ParacrossStatusDetails `protobuf:"bytes,4,opt,name=details,proto3" json:"details,omitempty"`
MainHeight int64 `protobuf:"varint,5,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"` SupervisionDetails *ParacrossStatusDetails `protobuf:"bytes,5,opt,name=supervisionDetails,proto3" json:"supervisionDetails,omitempty"`
MainHash []byte `protobuf:"bytes,6,opt,name=mainHash,proto3" json:"mainHash,omitempty"` MainHeight int64 `protobuf:"varint,6,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"`
BlockDetails *ParacrossStatusBlockDetails `protobuf:"bytes,7,opt,name=blockDetails,proto3" json:"blockDetails,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_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
...@@ -189,6 +189,13 @@ func (m *ParacrossHeightStatus) GetDetails() *ParacrossStatusDetails { ...@@ -189,6 +189,13 @@ func (m *ParacrossHeightStatus) GetDetails() *ParacrossStatusDetails {
return nil return nil
} }
func (m *ParacrossHeightStatus) GetSupervisionDetails() *ParacrossStatusDetails {
if m != nil {
return m.SupervisionDetails
}
return nil
}
func (m *ParacrossHeightStatus) GetMainHeight() int64 { func (m *ParacrossHeightStatus) GetMainHeight() int64 {
if m != nil { if m != nil {
return m.MainHeight return m.MainHeight
...@@ -4419,195 +4426,194 @@ func init() { ...@@ -4419,195 +4426,194 @@ func init() {
proto.RegisterType((*BlsPubKey)(nil), "types.BlsPubKey") proto.RegisterType((*BlsPubKey)(nil), "types.BlsPubKey")
} }
func init() { func init() { proto.RegisterFile("paracross.proto", fileDescriptor_6a397e38c9ea6747) }
proto.RegisterFile("paracross.proto", fileDescriptor_6a397e38c9ea6747)
}
var fileDescriptor_6a397e38c9ea6747 = []byte{ 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, 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, 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, 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, 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, 0x93, 0xb5, 0x11, 0x52, 0x38, 0x00, 0x37, 0x24, 0x2e, 0x48, 0x70, 0x80, 0x0b, 0xdc, 0x90, 0x38,
0xe2, 0xcc, 0x01, 0x89, 0x4b, 0xc4, 0x25, 0x1c, 0xb9, 0x71, 0x43, 0xe2, 0xc8, 0x3f, 0x80, 0xde, 0x71, 0xe6, 0x80, 0xc4, 0x25, 0xe2, 0x12, 0x8e, 0xdc, 0xb8, 0x81, 0x38, 0xf2, 0x0f, 0xa0, 0xf7,
0xab, 0xaa, 0xee, 0xaa, 0xea, 0x9e, 0xb1, 0x93, 0xdd, 0x0b, 0xb7, 0xa9, 0x57, 0xaf, 0xaa, 0xde, 0xaa, 0xaa, 0xbb, 0xaa, 0xba, 0x67, 0xec, 0x64, 0xf7, 0xc2, 0xad, 0xeb, 0xf5, 0xab, 0xaa, 0xf7,
0xf7, 0xd7, 0x34, 0xac, 0x4e, 0xc2, 0x34, 0x1c, 0xa4, 0x89, 0x10, 0xb7, 0x27, 0x69, 0x92, 0x25, 0xfd, 0xd5, 0x0d, 0xab, 0x93, 0x30, 0x0d, 0x07, 0x69, 0x22, 0xc4, 0xed, 0x49, 0x9a, 0x64, 0x89,
0x7e, 0x33, 0xbb, 0x9c, 0x70, 0xb1, 0xb5, 0x9e, 0xa5, 0x61, 0x2c, 0xc2, 0x41, 0x16, 0x25, 0xb1, 0xdf, 0xcc, 0x2e, 0x27, 0x5c, 0x6c, 0xad, 0x67, 0x69, 0x18, 0x8b, 0x70, 0x90, 0x45, 0x49, 0x2c,
0xdc, 0xd9, 0x5a, 0x1e, 0x24, 0xe3, 0x71, 0xbe, 0x5a, 0x3b, 0x19, 0x25, 0x83, 0x8f, 0x07, 0xe7, 0xdf, 0x6c, 0x2d, 0x0f, 0x92, 0xf1, 0x38, 0x5f, 0xad, 0x9d, 0x8c, 0x92, 0xc1, 0xc7, 0x83, 0xf3,
0x61, 0xa4, 0x20, 0xc1, 0x03, 0xd8, 0x7c, 0xa8, 0x2f, 0x3b, 0xca, 0xc2, 0x6c, 0x2a, 0xee, 0xf1, 0x30, 0x52, 0x90, 0xe0, 0x01, 0x6c, 0x3e, 0xd4, 0x87, 0x1d, 0x65, 0x61, 0x36, 0x15, 0xf7, 0x78,
0x2c, 0x8c, 0x46, 0xc2, 0xdf, 0x80, 0x66, 0x38, 0x1c, 0xa6, 0xa2, 0xe7, 0x6d, 0xd7, 0x77, 0x3a, 0x16, 0x46, 0x23, 0xe1, 0x6f, 0x40, 0x33, 0x1c, 0x0e, 0x53, 0xd1, 0xf3, 0xb6, 0xeb, 0x3b, 0x1d,
0x4c, 0x2e, 0xfc, 0x17, 0xa0, 0x43, 0x77, 0xf4, 0x43, 0x71, 0xde, 0xab, 0x6d, 0xd7, 0x77, 0x96, 0x26, 0x17, 0xfe, 0x0b, 0xd0, 0xa1, 0x33, 0xfa, 0xa1, 0x38, 0xef, 0xd5, 0xb6, 0xeb, 0x3b, 0xcb,
0x59, 0x01, 0x08, 0x3e, 0x82, 0xe7, 0x9d, 0xdb, 0xf6, 0x70, 0x4f, 0x5f, 0x79, 0x13, 0x20, 0xc7, 0xac, 0x00, 0x04, 0x1f, 0xc1, 0xf3, 0xce, 0x69, 0x7b, 0xf8, 0x4e, 0x1f, 0x79, 0x13, 0x20, 0xc7,
0x95, 0xf7, 0x2e, 0x33, 0x03, 0x82, 0x97, 0x67, 0x17, 0x8c, 0x8b, 0xe9, 0x28, 0x13, 0xfa, 0xf2, 0x95, 0xe7, 0x2e, 0x33, 0x03, 0x82, 0x87, 0x67, 0x17, 0x8c, 0x8b, 0xe9, 0x28, 0x13, 0xfa, 0xf0,
0x1c, 0x10, 0xfc, 0xba, 0x06, 0x37, 0xf2, 0xdb, 0xfb, 0x3c, 0x3a, 0x3b, 0xcf, 0xe4, 0x1b, 0xfe, 0x1c, 0x10, 0xfc, 0xab, 0x06, 0x37, 0xf2, 0xd3, 0xfb, 0x3c, 0x3a, 0x3b, 0xcf, 0xe4, 0x1d, 0xfe,
0x26, 0xb4, 0x04, 0xfd, 0xea, 0x79, 0xdb, 0xde, 0x4e, 0x93, 0xa9, 0x15, 0xb2, 0x90, 0x45, 0xd9, 0x26, 0xb4, 0x04, 0x3d, 0xf5, 0xbc, 0x6d, 0x6f, 0xa7, 0xc9, 0xd4, 0x0a, 0x59, 0xc8, 0xa2, 0x6c,
0x88, 0xf7, 0x6a, 0xdb, 0x1e, 0xb2, 0x40, 0x0b, 0xc4, 0x3e, 0xa7, 0xd3, 0xbd, 0xfa, 0xb6, 0xb7, 0xc4, 0x7b, 0xb5, 0x6d, 0x0f, 0x59, 0xa0, 0x05, 0x62, 0x9f, 0xd3, 0xee, 0x5e, 0x7d, 0xdb, 0xdb,
0x53, 0x67, 0x6a, 0xe5, 0x7f, 0x0d, 0x16, 0x87, 0x92, 0xd0, 0x5e, 0x63, 0xdb, 0xdb, 0x59, 0xda, 0xa9, 0x33, 0xb5, 0xf2, 0xbf, 0x06, 0x8b, 0x43, 0x49, 0x68, 0xaf, 0xb1, 0xed, 0xed, 0x2c, 0xed,
0x7d, 0xf1, 0x36, 0x89, 0xf5, 0x76, 0xb5, 0x80, 0x98, 0xc6, 0x46, 0xb6, 0xc6, 0x61, 0x14, 0x4b, 0xbe, 0x78, 0x9b, 0xc4, 0x7a, 0xbb, 0x5a, 0x40, 0x4c, 0x63, 0xfb, 0x87, 0xe0, 0x8b, 0xe9, 0x84,
0x92, 0x7a, 0x4d, 0xba, 0xd4, 0x80, 0xf8, 0x5b, 0xd0, 0xa6, 0x15, 0x8a, 0xac, 0xb5, 0xed, 0xed, 0xa7, 0x9f, 0x44, 0x22, 0x4a, 0x62, 0xf5, 0xba, 0xd7, 0xbc, 0xce, 0x19, 0x15, 0x1b, 0x51, 0x4a,
0x2c, 0xb3, 0x7c, 0xed, 0xbf, 0x03, 0xcb, 0x27, 0x86, 0x88, 0x7a, 0x8b, 0xf4, 0x72, 0x50, 0xfd, 0xe3, 0x30, 0x8a, 0x25, 0x87, 0xbd, 0x16, 0xd1, 0x68, 0x40, 0xfc, 0x2d, 0x68, 0xd3, 0x0a, 0x35,
0xb2, 0x29, 0x4c, 0x66, 0x9d, 0x0b, 0xfe, 0xe5, 0x41, 0xaf, 0x52, 0x38, 0x4c, 0x4c, 0x9e, 0x92, 0xb0, 0xb8, 0xed, 0xed, 0x2c, 0xb3, 0x7c, 0xed, 0xbf, 0x03, 0xcb, 0x27, 0x86, 0xc4, 0x7b, 0x6d,
0x7c, 0x6c, 0x36, 0x1b, 0x73, 0xd9, 0x6c, 0xd2, 0x85, 0x05, 0x9b, 0xdb, 0xb0, 0x84, 0x86, 0x18, 0x22, 0x22, 0xa8, 0x26, 0xc2, 0xd4, 0x0d, 0xb3, 0xf6, 0x05, 0xff, 0xf4, 0xa0, 0x57, 0x29, 0x6b,
0x65, 0x6f, 0x93, 0x49, 0xb5, 0xc8, 0xa4, 0x4c, 0x90, 0xbf, 0x03, 0xab, 0x72, 0xb9, 0x97, 0x9b, 0x26, 0x26, 0x4f, 0x49, 0xdc, 0x36, 0x9b, 0x8d, 0xb9, 0x6c, 0x36, 0xe9, 0xc0, 0x82, 0xcd, 0x6d,
0xd7, 0x22, 0x61, 0xb9, 0xe0, 0xe0, 0x57, 0x1e, 0xac, 0x3a, 0x82, 0x29, 0x38, 0xf1, 0xaa, 0x39, 0x58, 0x42, 0xbb, 0x8e, 0xb2, 0xb7, 0xc9, 0x42, 0x5b, 0x64, 0xa1, 0x26, 0xc8, 0xdf, 0x81, 0x55,
0xa9, 0x59, 0x9c, 0x58, 0x46, 0x5c, 0x27, 0x8d, 0x14, 0x80, 0x2f, 0xcc, 0xa7, 0xa1, 0xce, 0xe0, 0xb9, 0xdc, 0xcb, 0xad, 0x75, 0x91, 0xb0, 0x5c, 0x70, 0xf0, 0x2b, 0x0f, 0x56, 0x1d, 0xc1, 0x14,
0xf7, 0xa6, 0x1a, 0xf6, 0x93, 0x58, 0xf0, 0x58, 0x4c, 0xe7, 0x13, 0x89, 0xa2, 0x39, 0x2f, 0xde, 0x9c, 0x78, 0xd5, 0x9c, 0xd4, 0x2c, 0x4e, 0x2c, 0x9f, 0xa8, 0x93, 0x46, 0x0a, 0xc0, 0x17, 0xe6,
0x93, 0x94, 0x9a, 0x20, 0xff, 0x25, 0xe8, 0x0e, 0xe4, 0x55, 0x7d, 0x53, 0x2f, 0x36, 0xd0, 0xff, 0xd3, 0x50, 0x67, 0xf0, 0x3b, 0x53, 0x0d, 0xfb, 0x49, 0x2c, 0x78, 0x2c, 0xa6, 0xf3, 0x89, 0x44,
0x0a, 0xac, 0x29, 0x40, 0x21, 0xc1, 0x06, 0x3d, 0x54, 0x82, 0x07, 0x7f, 0xf2, 0xc0, 0x47, 0x32, 0xd1, 0x9c, 0x17, 0xf7, 0x49, 0x4a, 0x4d, 0x90, 0xff, 0x12, 0x74, 0x07, 0xf2, 0xa8, 0xbe, 0xa9,
0xdf, 0x4f, 0x86, 0x1c, 0xc5, 0xbf, 0x9f, 0xc4, 0xa7, 0xd1, 0xd9, 0x0c, 0x02, 0x57, 0xa0, 0x96, 0x17, 0x1b, 0xe8, 0x7f, 0x05, 0xd6, 0x14, 0xa0, 0x90, 0x60, 0x83, 0x2e, 0x2a, 0xc1, 0x83, 0x3f,
0x4c, 0x88, 0xae, 0x2e, 0xab, 0x25, 0x13, 0x5c, 0x47, 0x43, 0xa2, 0xa1, 0xc3, 0x6a, 0xd1, 0xd0, 0x7a, 0xe0, 0x23, 0x99, 0xef, 0x27, 0x43, 0x8e, 0xe2, 0xdf, 0x4f, 0xe2, 0xd3, 0xe8, 0x6c, 0x06,
0xf7, 0xa1, 0x81, 0xb1, 0x41, 0x3d, 0x46, 0xbf, 0xf1, 0xa6, 0x4f, 0xc2, 0xd1, 0x94, 0x93, 0x80, 0x81, 0x2b, 0x50, 0x4b, 0x26, 0x44, 0x57, 0x97, 0xd5, 0x92, 0x09, 0xae, 0xa3, 0x21, 0xd1, 0xd0,
0xba, 0x4c, 0x2e, 0xa4, 0x15, 0x44, 0xb1, 0x78, 0x27, 0x4d, 0x7e, 0xc0, 0x63, 0xf2, 0x05, 0x64, 0x61, 0xb5, 0x68, 0xe8, 0xfb, 0xd0, 0xc0, 0x50, 0xa3, 0x2e, 0xa3, 0x67, 0x3c, 0xe9, 0x93, 0x70,
0xb5, 0x00, 0x49, 0xcd, 0x88, 0x87, 0xd3, 0x93, 0xf7, 0xf8, 0x25, 0xf9, 0x42, 0x87, 0x15, 0x80, 0x34, 0xe5, 0x24, 0xa0, 0x2e, 0x93, 0x0b, 0x69, 0x05, 0x51, 0x2c, 0xde, 0x49, 0x93, 0x1f, 0xf0,
0xe0, 0x5b, 0x05, 0xd5, 0xdf, 0x4e, 0x32, 0x2e, 0x6d, 0x7f, 0x46, 0xa0, 0x42, 0x0a, 0x92, 0x8c, 0x58, 0x79, 0x8a, 0x09, 0x92, 0x9a, 0x11, 0x0f, 0xa7, 0x27, 0xef, 0xf1, 0x4b, 0xf2, 0x95, 0x0e,
0xcb, 0x38, 0xd2, 0x61, 0x72, 0x11, 0xfc, 0xd1, 0x83, 0x0d, 0x93, 0xf1, 0x83, 0xa1, 0xd2, 0x8d, 0x2b, 0x00, 0xc1, 0xb7, 0x0a, 0xaa, 0xbf, 0x9d, 0x64, 0x5c, 0xda, 0xfe, 0x8c, 0xb8, 0x87, 0x14,
0x66, 0xc2, 0x33, 0x98, 0xb8, 0x09, 0x30, 0x49, 0x93, 0x49, 0x22, 0xc2, 0xd1, 0xc1, 0x50, 0xf9, 0x24, 0x19, 0x97, 0x61, 0xa9, 0xc3, 0xe4, 0x22, 0xf8, 0x83, 0x07, 0x1b, 0x26, 0xe3, 0x07, 0x43,
0x88, 0x01, 0x41, 0xf3, 0x7a, 0x34, 0x8d, 0xb2, 0x03, 0x2d, 0x0c, 0xb5, 0x32, 0xdc, 0xad, 0x51, 0xa5, 0x1b, 0xcd, 0x84, 0x67, 0x30, 0x71, 0x13, 0x60, 0x92, 0x26, 0x93, 0x44, 0x84, 0xa3, 0x83,
0xed, 0x6e, 0x4d, 0x53, 0xbc, 0x16, 0xcb, 0x2d, 0x97, 0xe5, 0x5f, 0xd4, 0x60, 0x4d, 0x13, 0x9c, 0xa1, 0xf2, 0x11, 0x03, 0x82, 0xe6, 0xf5, 0x68, 0x1a, 0x65, 0x07, 0x5a, 0x18, 0x6a, 0x65, 0xb8,
0x13, 0x2b, 0x35, 0xe0, 0xe5, 0x1a, 0x28, 0x1e, 0xac, 0x55, 0x3f, 0x58, 0x37, 0x1f, 0xbc, 0x09, 0x5b, 0xa3, 0xda, 0xdd, 0x9a, 0xa6, 0x78, 0x2d, 0x96, 0x5b, 0x2e, 0xcb, 0xbf, 0xa8, 0xc1, 0x9a,
0x90, 0x85, 0xe9, 0x19, 0x27, 0xc7, 0x53, 0x5a, 0x33, 0x20, 0xae, 0x96, 0x9a, 0x65, 0x2d, 0xdd, 0x26, 0x38, 0x27, 0x56, 0x6a, 0xc0, 0xcb, 0x35, 0x50, 0x5c, 0x58, 0xab, 0xbe, 0xb0, 0x6e, 0x5e,
0xd1, 0xb2, 0x6d, 0x51, 0xb4, 0x7a, 0xce, 0x88, 0x56, 0xb6, 0x6e, 0x94, 0xd8, 0xd1, 0x65, 0x4e, 0x78, 0x13, 0x20, 0x0b, 0xd3, 0x33, 0x4e, 0x8e, 0xa7, 0xb4, 0x66, 0x40, 0x5c, 0x2d, 0x35, 0xcb,
0xd3, 0x64, 0x4c, 0x0f, 0x4a, 0xad, 0xe6, 0x6b, 0xc3, 0x49, 0xdb, 0x65, 0x27, 0xd5, 0x72, 0xe9, 0x5a, 0xba, 0xa3, 0x65, 0xdb, 0xa2, 0x68, 0xf5, 0x9c, 0x11, 0xad, 0x6c, 0xdd, 0x28, 0xb1, 0xa3,
0xb8, 0x72, 0xf9, 0xb3, 0x07, 0x37, 0x18, 0x1f, 0xf0, 0x68, 0x92, 0xe9, 0x67, 0x95, 0x11, 0x57, 0xcb, 0x9c, 0xa6, 0xc9, 0x98, 0x2e, 0x94, 0x5a, 0xcd, 0xd7, 0x86, 0x93, 0xb6, 0xcb, 0x4e, 0xaa,
0x69, 0xf2, 0x2e, 0xb4, 0x06, 0xb4, 0x4b, 0x02, 0x2a, 0x53, 0x5c, 0xf8, 0x00, 0x53, 0x88, 0xfe, 0xe5, 0xd2, 0x71, 0xe5, 0xf2, 0x27, 0x0f, 0x6e, 0x30, 0x3e, 0xe0, 0xd1, 0x24, 0xd3, 0xd7, 0x2a,
0x2b, 0xd0, 0x98, 0xa4, 0xfc, 0x13, 0x12, 0xdd, 0xd2, 0xee, 0xb3, 0xce, 0x01, 0xad, 0x0a, 0x46, 0x23, 0xae, 0xd2, 0xe4, 0x5d, 0x68, 0x0d, 0xe8, 0x2d, 0x09, 0xa8, 0x4c, 0x71, 0xe1, 0x03, 0x4c,
0x48, 0xfe, 0x5d, 0x58, 0x1c, 0x4c, 0xd3, 0x94, 0xc7, 0x99, 0x4a, 0x1d, 0x33, 0xf1, 0x35, 0x5e, 0x21, 0xfa, 0xaf, 0x40, 0x63, 0x92, 0xf2, 0x4f, 0x48, 0x74, 0x4b, 0xbb, 0xcf, 0x3a, 0x1b, 0xb4,
0xf0, 0x3b, 0x0f, 0x5e, 0x74, 0x18, 0x40, 0x2a, 0x10, 0xed, 0xc3, 0xc9, 0x30, 0xcc, 0xb8, 0x25, 0x2a, 0x18, 0x21, 0xf9, 0x77, 0x61, 0x71, 0x30, 0x4d, 0x53, 0x1e, 0x67, 0x2a, 0x13, 0xcd, 0xc4,
0x34, 0xcf, 0x11, 0xda, 0x1d, 0x45, 0x9d, 0x64, 0xe7, 0xf9, 0x0a, 0x76, 0x1c, 0x0a, 0xbf, 0x5a, 0xd7, 0x78, 0xc1, 0x6f, 0x3d, 0x78, 0xd1, 0x61, 0x00, 0xa9, 0x40, 0xb4, 0x0f, 0x27, 0xc3, 0x30,
0x50, 0x58, 0xbf, 0xfa, 0x4c, 0x4e, 0xe5, 0x7f, 0x3c, 0x78, 0xd6, 0xa1, 0x92, 0xb4, 0x9b, 0xc4, 0xe3, 0x96, 0xd0, 0x3c, 0x47, 0x68, 0x77, 0x14, 0x75, 0x92, 0x9d, 0xe7, 0x2b, 0xd8, 0x71, 0x28,
0xbc, 0x64, 0x85, 0xd5, 0xd9, 0xc4, 0xb6, 0xb6, 0x7a, 0xc9, 0xda, 0x70, 0x3f, 0xc9, 0xc2, 0x11, 0xfc, 0x6a, 0x41, 0x61, 0xfd, 0xea, 0x3d, 0x39, 0x95, 0xff, 0xf1, 0xe0, 0x59, 0x87, 0x4a, 0xd2,
0x5e, 0xad, 0x1d, 0xc6, 0x80, 0x50, 0x4d, 0x80, 0x2b, 0x7c, 0x96, 0x6c, 0xb1, 0xc9, 0x0a, 0x00, 0x6e, 0x12, 0xf3, 0x92, 0x15, 0x56, 0x67, 0x13, 0xdb, 0xda, 0xea, 0x25, 0x6b, 0xc3, 0xf7, 0x49,
0xc5, 0xe2, 0x44, 0x64, 0xb4, 0xd9, 0xa2, 0xcd, 0x7c, 0xed, 0xf7, 0x60, 0x11, 0xad, 0x8f, 0x89, 0x16, 0x8e, 0xf0, 0x68, 0xed, 0x30, 0x06, 0x84, 0x4a, 0x0c, 0x5c, 0xe1, 0xb5, 0x64, 0x8b, 0x4d,
0x4c, 0xd9, 0x9c, 0x5e, 0xe2, 0x9b, 0xc3, 0x24, 0xe6, 0x92, 0x59, 0x32, 0xbb, 0x26, 0x33, 0x20, 0x56, 0x00, 0x28, 0x16, 0x27, 0x22, 0xa3, 0x97, 0x2d, 0x7a, 0x99, 0xaf, 0xfd, 0x1e, 0x2c, 0xa2,
0xa8, 0x9b, 0x67, 0x34, 0xbb, 0xef, 0xa6, 0xc9, 0x74, 0xf2, 0x44, 0xf1, 0x31, 0x8f, 0x4f, 0xd2, 0xf5, 0x31, 0x91, 0x29, 0x9b, 0xd3, 0x4b, 0xbc, 0x73, 0x98, 0xc4, 0x5c, 0x32, 0x4b, 0x66, 0xd7,
0xd5, 0x54, 0x7c, 0xba, 0xda, 0xcb, 0xa8, 0x5a, 0x52, 0xf6, 0x2e, 0x54, 0x64, 0x30, 0x20, 0xc1, 0x64, 0x06, 0x04, 0x75, 0xf3, 0x8c, 0x66, 0xf7, 0xdd, 0x34, 0x99, 0x4e, 0x9e, 0x28, 0x3e, 0xe6,
0xbf, 0x5d, 0x2a, 0x9f, 0x4a, 0x74, 0xd8, 0x86, 0xa5, 0x42, 0x3b, 0x9a, 0x66, 0x13, 0x74, 0x0d, 0xf1, 0x49, 0xba, 0x9a, 0x8a, 0x4f, 0x57, 0x7b, 0x19, 0x15, 0x5f, 0xca, 0xde, 0x85, 0x8a, 0x0c,
0xca, 0x4d, 0xcb, 0x6d, 0xcd, 0x74, 0xf7, 0x45, 0xb7, 0xba, 0x30, 0xb8, 0x6d, 0x97, 0xb8, 0xfd, 0x06, 0x24, 0xf8, 0xb7, 0x4b, 0xe5, 0x53, 0x89, 0x0e, 0xdb, 0xb0, 0x54, 0x68, 0x47, 0xd3, 0x6c,
0xcc, 0x83, 0x2d, 0xc7, 0x12, 0x4d, 0xd5, 0x54, 0x79, 0xfd, 0xae, 0xe3, 0xf5, 0x5b, 0x8e, 0xc9, 0x82, 0xae, 0x41, 0xb9, 0x69, 0xb9, 0xad, 0x99, 0xee, 0xbe, 0xe8, 0x56, 0x17, 0x06, 0xb7, 0xed,
0x1b, 0xe7, 0x73, 0xb7, 0xbf, 0x6d, 0xb9, 0x7d, 0xe5, 0x09, 0xcb, 0xaf, 0xde, 0x70, 0x3d, 0x7f, 0x12, 0xb7, 0x9f, 0x79, 0xb0, 0xe5, 0x58, 0xa2, 0xa9, 0x9a, 0x2a, 0xaf, 0xdf, 0x75, 0xbc, 0x7e,
0xde, 0x91, 0xdc, 0xad, 0x7e, 0xea, 0xc1, 0x06, 0xe3, 0x8f, 0xf2, 0x4a, 0x81, 0x42, 0x44, 0x7c, 0xcb, 0x31, 0x79, 0x63, 0x7f, 0xee, 0xf6, 0xb7, 0x2d, 0xb7, 0xaf, 0xdc, 0x61, 0xf9, 0xd5, 0x1b,
0x9a, 0xcc, 0xb6, 0xb0, 0x48, 0x27, 0x20, 0x33, 0xe3, 0xd6, 0x0d, 0x66, 0x67, 0x25, 0x1d, 0x2b, 0xae, 0xe7, 0xcf, 0xdb, 0x92, 0xbb, 0xd5, 0x4f, 0x3d, 0xd8, 0x60, 0xfc, 0x51, 0x5e, 0x29, 0x50,
0x8c, 0x36, 0xdd, 0x30, 0xba, 0x0f, 0x9b, 0x8c, 0x8b, 0x89, 0x45, 0x88, 0xd4, 0xf2, 0xff, 0x43, 0x88, 0x88, 0x4f, 0x93, 0xd9, 0x16, 0x16, 0xe9, 0x04, 0x64, 0x66, 0xdc, 0xba, 0xc1, 0xec, 0xac,
0x3d, 0x1a, 0xca, 0x9c, 0x3a, 0x27, 0x9c, 0x21, 0x4e, 0xf0, 0x2e, 0xc6, 0x08, 0xe7, 0x12, 0x62, 0xa4, 0x63, 0x85, 0xd1, 0xa6, 0x1b, 0x46, 0xf7, 0x61, 0x93, 0x71, 0x31, 0xb1, 0x08, 0x91, 0x5a,
0x5b, 0xf8, 0xb7, 0xcc, 0x5b, 0xe6, 0x89, 0x86, 0x2e, 0x9a, 0xc8, 0x5c, 0xb7, 0x17, 0xc5, 0xc3, 0xfe, 0x7f, 0xa8, 0x47, 0x43, 0x99, 0x53, 0xe7, 0x84, 0x33, 0xc4, 0x09, 0xde, 0xc5, 0x18, 0xe1,
0xc3, 0x28, 0xe6, 0xe9, 0xfe, 0x78, 0x48, 0x76, 0x11, 0xc5, 0xc3, 0xb7, 0xa9, 0xa9, 0x51, 0xf5, 0x1c, 0x42, 0x6c, 0x0b, 0xff, 0x96, 0x79, 0xca, 0x3c, 0xd1, 0xd0, 0x41, 0x13, 0x99, 0xeb, 0xf6,
0xab, 0x01, 0x21, 0xfe, 0xa2, 0x78, 0xb8, 0x8f, 0xe6, 0xa7, 0x8a, 0xa7, 0x02, 0x50, 0x44, 0x1f, 0xa2, 0x78, 0x78, 0x18, 0xc5, 0x3c, 0xdd, 0x1f, 0x0f, 0xc9, 0x2e, 0xa2, 0x78, 0xf8, 0x36, 0xf5,
0x7c, 0xcf, 0x8e, 0x3e, 0x08, 0x09, 0xfe, 0xea, 0xc1, 0xba, 0xf5, 0x24, 0x69, 0x61, 0x46, 0x31, 0x48, 0xaa, 0x7e, 0x35, 0x20, 0xc4, 0x5f, 0x14, 0x0f, 0xf7, 0xd1, 0xfc, 0x54, 0xf1, 0x54, 0x00,
0x80, 0xd7, 0x1e, 0x99, 0x9e, 0x64, 0x40, 0x6c, 0x3a, 0xea, 0xf3, 0xe9, 0x68, 0xb8, 0x74, 0xe4, 0x8a, 0xe8, 0x83, 0xf7, 0xd9, 0xd1, 0x07, 0x21, 0xc1, 0x5f, 0x3c, 0x58, 0xb7, 0xae, 0x24, 0x2d,
0x15, 0xe9, 0x71, 0x34, 0xe6, 0xca, 0xa3, 0x0a, 0x00, 0x7a, 0x9c, 0x2c, 0x4f, 0xa5, 0xe3, 0xa8, 0xcc, 0x28, 0x06, 0xf0, 0xd8, 0x23, 0xd3, 0x93, 0x0c, 0x88, 0x4d, 0x47, 0x7d, 0x3e, 0x1d, 0x0d,
0xba, 0xc9, 0x00, 0x05, 0x3f, 0xf7, 0xa0, 0x67, 0x78, 0xc7, 0xd5, 0xec, 0xdc, 0xb2, 0x12, 0x48, 0x97, 0x8e, 0xbc, 0x22, 0x3d, 0x8e, 0xc6, 0x5c, 0x79, 0x54, 0x01, 0x40, 0x8f, 0x93, 0xe5, 0xa9,
0xcf, 0xd0, 0x8c, 0x75, 0x56, 0x59, 0xf9, 0xae, 0x9b, 0x3d, 0x66, 0x1f, 0xc8, 0x6d, 0xbc, 0x5f, 0xd9, 0x61, 0x98, 0xa0, 0xe0, 0xe7, 0x1e, 0xf4, 0x0c, 0xef, 0xb8, 0x9a, 0x9d, 0x5b, 0x56, 0x02,
0x14, 0x2e, 0x88, 0xf1, 0x20, 0x12, 0x94, 0xd3, 0xc5, 0x74, 0xc2, 0x53, 0x92, 0x82, 0x24, 0xa7, 0xe9, 0x19, 0x9a, 0xb1, 0xf6, 0x2a, 0x2b, 0xdf, 0x75, 0xb3, 0xc7, 0xec, 0x0d, 0xb9, 0x8d, 0xf7,
0x00, 0xa0, 0x09, 0x8f, 0xf1, 0x1e, 0x5d, 0xb3, 0xa9, 0x55, 0xf0, 0x69, 0x29, 0x53, 0xea, 0x0b, 0x8b, 0xc2, 0x05, 0x31, 0x1e, 0x44, 0x82, 0x72, 0x3a, 0xf5, 0x4f, 0x24, 0x05, 0x49, 0x4e, 0x01,
0x55, 0xa6, 0xd4, 0xb9, 0xda, 0xab, 0xcc, 0xbd, 0x1a, 0xb9, 0x9c, 0xab, 0x6b, 0xf3, 0xf1, 0x73, 0x40, 0x13, 0x1e, 0xe3, 0x39, 0xba, 0x66, 0x53, 0xab, 0xe0, 0xd3, 0x52, 0xa6, 0xd4, 0x07, 0xaa,
0x56, 0x1e, 0xa3, 0xb7, 0x4a, 0x03, 0xb7, 0xd8, 0x79, 0x05, 0x1a, 0xa3, 0x48, 0x64, 0x57, 0xbe, 0x4c, 0xa9, 0x73, 0xb5, 0x57, 0x99, 0x7b, 0x35, 0x72, 0x39, 0x57, 0xd7, 0xe6, 0xe3, 0xe7, 0xac,
0x8b, 0x48, 0x28, 0x43, 0xdd, 0x5e, 0xd6, 0xc8, 0x1d, 0xe6, 0xc8, 0x50, 0x21, 0x06, 0x3f, 0xd1, 0x3c, 0x46, 0x6f, 0x95, 0x06, 0x6e, 0xb1, 0xf3, 0x0a, 0x34, 0x46, 0x91, 0xc8, 0xae, 0xbc, 0x17,
0xe6, 0x89, 0xaa, 0xde, 0x3d, 0x0c, 0xa3, 0xf8, 0x30, 0x9c, 0x18, 0x21, 0xd4, 0x9b, 0xdd, 0xd6, 0x91, 0x50, 0x86, 0xba, 0x5b, 0xad, 0x91, 0x3b, 0xcc, 0x91, 0xa1, 0x42, 0x0c, 0x7e, 0xa2, 0xcd,
0xd4, 0xb4, 0xab, 0x57, 0xb7, 0x35, 0xf5, 0xb9, 0x6d, 0x4d, 0xc3, 0x6e, 0xdf, 0x82, 0x7b, 0xb2, 0x13, 0x55, 0xbd, 0x7b, 0x18, 0x46, 0xf1, 0x61, 0x38, 0x31, 0x42, 0xa8, 0x37, 0xbb, 0xad, 0xa9,
0xf0, 0x2e, 0xc8, 0x20, 0xbb, 0xba, 0x0d, 0xcd, 0x28, 0xe3, 0x63, 0xed, 0xde, 0x16, 0x3f, 0x26, 0x69, 0x57, 0xaf, 0x6e, 0x6b, 0xea, 0x73, 0xdb, 0x9a, 0x86, 0xdd, 0xbe, 0x05, 0xf7, 0x64, 0xe1,
0xc1, 0x4c, 0xa2, 0x05, 0xff, 0xac, 0xcb, 0x84, 0x95, 0x07, 0x09, 0xe5, 0x3a, 0x2f, 0x41, 0x17, 0x5d, 0x90, 0x41, 0x76, 0x75, 0x1b, 0x9a, 0x51, 0xc6, 0xc7, 0xda, 0xbd, 0x2d, 0x7e, 0x4c, 0x82,
0x5f, 0x2a, 0xda, 0x16, 0x8f, 0xba, 0x2a, 0x1b, 0x88, 0x0d, 0x62, 0x01, 0x30, 0x7b, 0x25, 0x17, 0x99, 0x44, 0x0b, 0xfe, 0x51, 0x97, 0x09, 0x2b, 0x0f, 0x12, 0xca, 0x75, 0x5e, 0x82, 0x2e, 0xde,
0x3c, 0x23, 0xb1, 0x15, 0x52, 0x6b, 0x58, 0x52, 0x0b, 0x60, 0x79, 0x92, 0xf2, 0xe2, 0x71, 0xd9, 0x54, 0xb4, 0x2d, 0x1e, 0x75, 0x55, 0x36, 0x10, 0x1b, 0xc4, 0x02, 0x60, 0xf6, 0x4a, 0x2e, 0x78,
0xd2, 0x59, 0x30, 0x5b, 0xb2, 0x2d, 0xb7, 0x61, 0x94, 0x37, 0x20, 0x33, 0x5c, 0xf5, 0xad, 0xfa, 0x46, 0x62, 0x2b, 0xa4, 0xd6, 0xb0, 0xa4, 0x16, 0xc0, 0xf2, 0x24, 0xe5, 0xc5, 0xe5, 0xb2, 0xa5,
0x86, 0x1c, 0x46, 0x96, 0x9f, 0x23, 0xb4, 0xe5, 0x0d, 0x39, 0x00, 0x65, 0x9f, 0x5d, 0xec, 0x27, 0xb3, 0x60, 0xb6, 0x64, 0x5b, 0x6e, 0xc3, 0x28, 0x4f, 0x40, 0x66, 0xb8, 0xd1, 0xe3, 0x5b, 0x30,
0xd3, 0x38, 0x13, 0x54, 0xea, 0x76, 0x59, 0xbe, 0x96, 0x7b, 0x72, 0x06, 0xd2, 0x03, 0xd9, 0x6e, 0xb2, 0xfc, 0x1c, 0xa1, 0x2d, 0x4f, 0xc8, 0x01, 0x28, 0xfb, 0xec, 0x62, 0x3f, 0x99, 0xc6, 0x99,
0xea, 0x35, 0x96, 0x38, 0xd9, 0x85, 0x9c, 0xa6, 0x2c, 0xd1, 0xb8, 0x44, 0x2f, 0xa9, 0x67, 0x44, 0xa0, 0x52, 0xb7, 0xcb, 0xf2, 0xb5, 0x7c, 0x27, 0x47, 0x2a, 0x3d, 0x90, 0xed, 0xa6, 0x5e, 0x63,
0x31, 0x1f, 0xeb, 0xa3, 0xcb, 0x52, 0xa6, 0x16, 0x10, 0x29, 0x57, 0x00, 0x79, 0x49, 0x97, 0x2e, 0x89, 0x93, 0x5d, 0xc8, 0xe1, 0xcc, 0x12, 0x4d, 0x5f, 0xf4, 0x92, 0x7a, 0x46, 0x14, 0xf3, 0xb1,
0xb1, 0x60, 0xfe, 0x2d, 0x58, 0x8f, 0x93, 0x78, 0x9f, 0x9a, 0xf0, 0x63, 0x4d, 0xe4, 0x0a, 0x11, 0xde, 0xba, 0x2c, 0x65, 0x6a, 0x01, 0x91, 0x72, 0x05, 0x90, 0x87, 0x74, 0xe9, 0x10, 0x0b, 0xe6,
0x59, 0xde, 0x08, 0xf6, 0x60, 0xfd, 0x88, 0x8f, 0x4e, 0x55, 0xeb, 0x7b, 0x94, 0x85, 0x67, 0x5c, 0xdf, 0x82, 0xf5, 0x38, 0x89, 0xf7, 0xa9, 0x09, 0x3f, 0xd6, 0x44, 0xae, 0x10, 0x91, 0xe5, 0x17,
0xf8, 0xaf, 0xda, 0x86, 0xa2, 0x1d, 0xc5, 0x45, 0xd4, 0x76, 0xf2, 0x00, 0xd6, 0xdc, 0x2d, 0x0c, 0xc1, 0x1e, 0xac, 0x1f, 0xf1, 0xd1, 0xa9, 0x6a, 0x7d, 0x8f, 0xb2, 0xf0, 0x8c, 0x0b, 0xff, 0x55,
0x81, 0x22, 0x0b, 0xd3, 0xac, 0x6f, 0x1a, 0xbe, 0x09, 0x42, 0xfd, 0xf2, 0x38, 0x3c, 0x51, 0xf5, 0xdb, 0x50, 0xb4, 0xa3, 0xb8, 0x88, 0xda, 0x4e, 0x1e, 0xc0, 0x9a, 0xfb, 0x0a, 0x43, 0xa0, 0xc8,
0x67, 0x97, 0xa9, 0x55, 0xf0, 0x0f, 0x0f, 0x36, 0xdc, 0xeb, 0xc8, 0x7c, 0xe7, 0xd7, 0x49, 0xdd, 0xc2, 0x34, 0xeb, 0x9b, 0x86, 0x6f, 0x82, 0x50, 0xbf, 0x3c, 0x0e, 0x4f, 0x54, 0xfd, 0xd9, 0x65,
0x3c, 0x83, 0xbe, 0x0a, 0x4d, 0x81, 0x87, 0x9c, 0x56, 0xa0, 0x4c, 0x3d, 0x61, 0x59, 0xc5, 0x4f, 0x6a, 0x15, 0xfc, 0xdd, 0x83, 0x0d, 0xf7, 0x38, 0x32, 0xdf, 0xf9, 0x75, 0x52, 0x37, 0xcf, 0xa0,
0xc3, 0x29, 0x7e, 0x6e, 0x02, 0xf0, 0x0b, 0x3e, 0xb0, 0x27, 0x45, 0x05, 0xe4, 0x0b, 0x37, 0x56, 0xaf, 0x42, 0x53, 0xe0, 0x26, 0xa7, 0x15, 0x28, 0x53, 0x4f, 0x58, 0x56, 0xf1, 0xd3, 0x70, 0x8a,
0x01, 0x87, 0xcd, 0x07, 0xc9, 0x20, 0x1c, 0x69, 0x62, 0x0a, 0xee, 0xee, 0x6a, 0xaa, 0x3d, 0xab, 0x9f, 0x9b, 0x00, 0xfc, 0x82, 0x0f, 0x14, 0x13, 0x32, 0xce, 0x1b, 0x90, 0x2f, 0xdc, 0x58, 0x05,
0xdc, 0xaf, 0x92, 0x84, 0xa6, 0x9c, 0xac, 0xe9, 0x20, 0x1e, 0xf2, 0x0b, 0x15, 0x3d, 0xf4, 0x32, 0x1c, 0x36, 0x1f, 0x24, 0x83, 0x70, 0xa4, 0x89, 0x29, 0xb8, 0xbb, 0xab, 0xa9, 0xf6, 0xac, 0x72,
0x78, 0x13, 0x56, 0x64, 0x9d, 0x84, 0x14, 0x54, 0x0a, 0x2f, 0x6f, 0xf8, 0x6b, 0x46, 0xc3, 0x1f, 0xbf, 0x4a, 0x12, 0x9a, 0x72, 0xb2, 0xa6, 0x83, 0x78, 0xc8, 0x2f, 0x54, 0xf4, 0xd0, 0xcb, 0xe0,
0x04, 0xb0, 0x26, 0xcf, 0xed, 0x87, 0xf1, 0x80, 0x8f, 0xaa, 0x4e, 0x06, 0x9f, 0xab, 0x71, 0x0e, 0x4d, 0x58, 0x91, 0x75, 0x12, 0x52, 0x50, 0x29, 0xbc, 0xbc, 0xe1, 0xaf, 0x19, 0x0d, 0x7f, 0x10,
0x91, 0x73, 0x55, 0xa1, 0x9d, 0x5d, 0xea, 0x42, 0x3b, 0xbb, 0x44, 0x69, 0x49, 0x16, 0x61, 0xae, 0xc0, 0x9a, 0xdc, 0xb7, 0x1f, 0xc6, 0x03, 0x3e, 0xaa, 0xda, 0x19, 0x7c, 0xae, 0xc6, 0x39, 0x44,
0x62, 0xfa, 0x0b, 0x9a, 0xc1, 0x57, 0xa0, 0x81, 0x62, 0xeb, 0x2d, 0x11, 0xfe, 0x0d, 0x85, 0x6f, 0xce, 0x55, 0x85, 0x76, 0x76, 0xa9, 0x0b, 0xed, 0xec, 0x12, 0xa5, 0x25, 0x59, 0x84, 0xb9, 0x8a,
0x73, 0xd6, 0x5f, 0x60, 0x84, 0x44, 0x3d, 0x23, 0x51, 0x4d, 0xae, 0x53, 0x5c, 0xef, 0x32, 0xd4, 0xe9, 0x2f, 0x68, 0x06, 0x5f, 0x81, 0x06, 0x8a, 0xad, 0xb7, 0x44, 0xf8, 0x37, 0x14, 0xbe, 0xcd,
0x5f, 0x60, 0x0a, 0x71, 0x6f, 0x51, 0x09, 0x21, 0xf8, 0x71, 0x51, 0xac, 0x5a, 0x9a, 0x51, 0xec, 0x59, 0x7f, 0x81, 0x11, 0x12, 0xf5, 0x8c, 0x44, 0x35, 0xb9, 0x4e, 0x71, 0xbc, 0xcb, 0x50, 0x7f,
0xdd, 0xb1, 0xf2, 0xd5, 0x5c, 0xd5, 0x94, 0xba, 0xb7, 0xda, 0xd5, 0x67, 0xf2, 0xbc, 0xf5, 0xb9, 0x81, 0x29, 0xc4, 0xbd, 0x45, 0x25, 0x84, 0xe0, 0xc7, 0x45, 0xb1, 0x6a, 0x69, 0x46, 0xb1, 0x77,
0x07, 0x2f, 0x54, 0x91, 0x31, 0xb3, 0x85, 0xcb, 0x4d, 0xbd, 0x76, 0x2d, 0x53, 0xb7, 0x7b, 0xb7, 0xc7, 0xca, 0x57, 0x73, 0x55, 0x53, 0xea, 0xde, 0x6a, 0x57, 0xef, 0xc9, 0xf3, 0xd6, 0xe7, 0x1e,
0xfa, 0xfc, 0xde, 0xad, 0x31, 0xaf, 0x77, 0x6b, 0xce, 0xee, 0xdd, 0x5a, 0x56, 0xef, 0x16, 0x7c, 0xbc, 0x50, 0x45, 0xc6, 0xcc, 0x16, 0x2e, 0x37, 0xf5, 0xda, 0xb5, 0x4c, 0xdd, 0xee, 0xdd, 0xea,
0x0a, 0xcf, 0x57, 0xb1, 0x24, 0x54, 0x29, 0x70, 0xcb, 0x12, 0x6d, 0x6f, 0x06, 0x03, 0xa2, 0x5c, 0xf3, 0x7b, 0xb7, 0xc6, 0xbc, 0xde, 0xad, 0x39, 0xbb, 0x77, 0x6b, 0x59, 0xbd, 0x5b, 0xf0, 0x29,
0xd7, 0xd4, 0xae, 0x38, 0x90, 0x0b, 0xf5, 0xb7, 0x1e, 0xf8, 0x8c, 0x3f, 0xfa, 0x60, 0xca, 0xd3, 0x3c, 0x5f, 0xc5, 0x92, 0x50, 0xa5, 0xc0, 0x2d, 0x4b, 0xb4, 0xbd, 0x19, 0x0c, 0x88, 0x72, 0x5d,
0x4b, 0x44, 0x53, 0x31, 0xce, 0x9e, 0xb1, 0x16, 0xd1, 0xc3, 0xad, 0xdd, 0x37, 0xa0, 0x39, 0xc0, 0x53, 0xbb, 0x62, 0x43, 0x2e, 0xd4, 0xdf, 0x78, 0xe0, 0x33, 0xfe, 0xe8, 0x83, 0x29, 0x4f, 0x2f,
0x50, 0xa9, 0xc4, 0x25, 0x17, 0x28, 0xa9, 0x61, 0x94, 0x72, 0x59, 0xe4, 0x2a, 0x49, 0xe5, 0x00, 0x11, 0x4d, 0xc5, 0x38, 0x7b, 0xc6, 0x5a, 0x44, 0x0f, 0xb7, 0x76, 0xdf, 0x80, 0xe6, 0x00, 0x43,
0x23, 0x75, 0x35, 0xad, 0xd4, 0xb5, 0x01, 0xcd, 0x88, 0xdc, 0x55, 0xb6, 0xbe, 0x72, 0x11, 0x7c, 0xa5, 0x12, 0x97, 0x5c, 0xa0, 0xa4, 0x86, 0x51, 0xca, 0x65, 0x91, 0xab, 0x24, 0x95, 0x03, 0x8c,
0x80, 0xd5, 0xca, 0x64, 0x74, 0xe9, 0x52, 0xf8, 0x16, 0xa5, 0x20, 0x69, 0x23, 0x2a, 0x12, 0xcf, 0xd4, 0xd5, 0xb4, 0x52, 0xd7, 0x06, 0x34, 0x23, 0x72, 0x57, 0xd9, 0xfa, 0xca, 0x45, 0xf0, 0x01,
0x35, 0xa3, 0x02, 0x3b, 0xf8, 0x9e, 0xf1, 0x2f, 0xc1, 0xbe, 0x1a, 0xc7, 0x0a, 0x5d, 0x5b, 0x8a, 0x56, 0x2b, 0x93, 0xd1, 0xa5, 0x4b, 0xe1, 0x5b, 0x94, 0x82, 0xa4, 0x8d, 0xa8, 0x48, 0x3c, 0xd7,
0xe8, 0x2c, 0x56, 0x29, 0x9b, 0x7e, 0xa3, 0x62, 0xa9, 0xc7, 0x3d, 0x0c, 0x65, 0x5b, 0xbc, 0xcc, 0x8c, 0x0a, 0xec, 0xe0, 0x7b, 0xc6, 0x47, 0x87, 0x7d, 0x35, 0x8e, 0x15, 0xba, 0xb6, 0x14, 0xd1,
0xf2, 0x75, 0xd1, 0x0c, 0xd7, 0x8d, 0x61, 0x5d, 0xf0, 0x43, 0x63, 0xb2, 0x2f, 0xef, 0x57, 0xd5, 0x59, 0xac, 0x52, 0x36, 0x3d, 0xa3, 0x62, 0xa9, 0xc7, 0x3d, 0x0c, 0x65, 0x5b, 0xbc, 0xcc, 0xf2,
0xfd, 0xae, 0x25, 0x55, 0xbb, 0x85, 0x70, 0xca, 0x88, 0x5c, 0xe2, 0x77, 0xa0, 0x7e, 0x32, 0x12, 0x75, 0xd1, 0x0c, 0xd7, 0x8d, 0x61, 0x5d, 0xf0, 0x43, 0xe3, 0x43, 0x81, 0x3c, 0x5f, 0x55, 0xf7,
0x4a, 0xa1, 0xa5, 0x19, 0xbe, 0x45, 0x3e, 0x43, 0xcc, 0x20, 0x93, 0x33, 0x41, 0xda, 0xa6, 0x22, 0xbb, 0x96, 0x54, 0xed, 0x16, 0xc2, 0x29, 0x23, 0x72, 0x89, 0xdf, 0x81, 0xfa, 0xc9, 0x48, 0x28,
0xec, 0x09, 0x1e, 0xdf, 0x81, 0xd5, 0x48, 0x18, 0xe2, 0x54, 0xd9, 0xa4, 0xcd, 0x5c, 0x70, 0xf0, 0x85, 0x96, 0xc6, 0xf9, 0x16, 0xf9, 0x0c, 0x31, 0x83, 0x4c, 0xce, 0x04, 0xe9, 0x35, 0x15, 0x61,
0x4b, 0x0f, 0xfc, 0x7d, 0xbc, 0xe5, 0x6d, 0x21, 0x78, 0x76, 0x9c, 0x86, 0xb1, 0x38, 0xe5, 0x29, 0x4f, 0x70, 0xf9, 0x0e, 0xac, 0x46, 0xc2, 0x10, 0xa7, 0xca, 0x26, 0x6d, 0xe6, 0x82, 0x83, 0x5f,
0x5a, 0x42, 0x88, 0x80, 0xfb, 0x17, 0x7c, 0xa0, 0x4b, 0xe4, 0x1c, 0x80, 0x69, 0x90, 0x16, 0x47, 0x7a, 0xe0, 0xef, 0xe3, 0x29, 0x6f, 0x0b, 0xc1, 0xb3, 0xe3, 0x34, 0x8c, 0xc5, 0x29, 0x4f, 0xd1,
0x97, 0xe3, 0x93, 0x64, 0xa4, 0xcc, 0xca, 0x04, 0xa1, 0xad, 0x84, 0xe3, 0xdc, 0xc0, 0xea, 0x4c, 0x12, 0x42, 0x04, 0xdc, 0xbf, 0xe0, 0x03, 0x5d, 0x22, 0xe7, 0x00, 0x4c, 0x83, 0xb4, 0x38, 0xba,
0xad, 0x10, 0x9e, 0x25, 0x46, 0x52, 0x52, 0x2b, 0x54, 0x60, 0xac, 0x3d, 0xb0, 0xc3, 0xe8, 0x77, 0x1c, 0x9f, 0x24, 0x23, 0x65, 0x56, 0x26, 0x08, 0x6d, 0x25, 0x1c, 0xe7, 0x06, 0x56, 0x67, 0x6a,
0xf0, 0x9b, 0x96, 0x31, 0x61, 0x57, 0xc2, 0x78, 0x13, 0x9b, 0x69, 0x14, 0x9d, 0x12, 0xc6, 0x0b, 0x85, 0xf0, 0x2c, 0x31, 0x92, 0x92, 0x5a, 0xa1, 0x02, 0x63, 0xed, 0x81, 0x1d, 0x46, 0xcf, 0xc1,
0xd5, 0x82, 0x95, 0xd8, 0x14, 0x13, 0x69, 0xed, 0xbf, 0x0e, 0x4d, 0x2a, 0xe3, 0x2b, 0xc6, 0x4e, 0xaf, 0x5b, 0xc6, 0x84, 0x5d, 0x09, 0xe3, 0x4d, 0x6c, 0xa6, 0x51, 0x74, 0x4a, 0x18, 0x2f, 0x54,
0xae, 0xc0, 0x31, 0x50, 0x13, 0xae, 0xff, 0x0d, 0xe8, 0x86, 0xa6, 0x54, 0x54, 0x6f, 0xad, 0x23, 0x0b, 0x56, 0x62, 0x53, 0x4c, 0xa4, 0xb5, 0xff, 0x3a, 0x34, 0xa9, 0x8c, 0xaf, 0x18, 0x3b, 0xb9,
0x36, 0x49, 0x4c, 0xe8, 0xcd, 0xfe, 0x02, 0xb3, 0xb1, 0xf3, 0xe3, 0xdf, 0x89, 0xb2, 0xf3, 0x61, 0x02, 0xc7, 0x40, 0x4d, 0xb8, 0xfe, 0x37, 0xa0, 0x1b, 0x9a, 0x52, 0x51, 0xbd, 0xb5, 0x8e, 0xd8,
0x1a, 0x3e, 0x26, 0xe6, 0xdc, 0xe3, 0x7a, 0x33, 0x3f, 0xae, 0x01, 0xfe, 0xeb, 0xd0, 0xce, 0xf4, 0x24, 0x31, 0xa1, 0x5f, 0xf6, 0x17, 0x98, 0x8d, 0x9d, 0x6f, 0xff, 0x4e, 0x94, 0x9d, 0x0f, 0xd3,
0xc3, 0xad, 0xf9, 0x0f, 0xe7, 0x88, 0x78, 0xe8, 0xb1, 0x7e, 0x6e, 0x71, 0xfe, 0x73, 0x39, 0xa2, 0xf0, 0xb1, 0xfa, 0xb4, 0x63, 0x6f, 0xd7, 0x2f, 0xf3, 0xed, 0x1a, 0xe0, 0xbf, 0x0e, 0xed, 0x4c,
0x7f, 0x1f, 0x56, 0xf4, 0x05, 0xc7, 0x09, 0x69, 0xbc, 0x6d, 0x49, 0xc9, 0x7e, 0x4f, 0xa2, 0xf4, 0x5f, 0xdc, 0x9a, 0x7f, 0x71, 0x8e, 0x88, 0x9b, 0x1e, 0xeb, 0xeb, 0x16, 0xe7, 0x5f, 0x97, 0x23,
0x17, 0x98, 0x73, 0xc8, 0xff, 0x3a, 0x40, 0x9c, 0x0f, 0x40, 0xa9, 0x80, 0x9c, 0x37, 0xe2, 0xec, 0xfa, 0xf7, 0x61, 0x45, 0x1f, 0x70, 0x9c, 0x90, 0xc6, 0xdb, 0x96, 0x94, 0xec, 0xfb, 0x24, 0x4a,
0x2f, 0x30, 0x03, 0xdd, 0x7f, 0x07, 0x56, 0x63, 0x7b, 0x18, 0xa2, 0xf2, 0xe9, 0x9c, 0x71, 0x49, 0x7f, 0x81, 0x39, 0x9b, 0xfc, 0xaf, 0x03, 0xc4, 0xf9, 0x00, 0x94, 0x0a, 0xc8, 0x79, 0x23, 0xce,
0x7f, 0x81, 0xb9, 0x87, 0xfc, 0x3d, 0x58, 0x15, 0x3a, 0xd8, 0xa8, 0x7b, 0x64, 0x9e, 0xdd, 0x34, 0xfe, 0x02, 0x33, 0xd0, 0xfd, 0x77, 0x60, 0x35, 0xb6, 0x87, 0x21, 0x2a, 0x9f, 0xce, 0x19, 0x97,
0xee, 0x31, 0x76, 0xf1, 0x0e, 0xe7, 0x80, 0xff, 0x1e, 0xf8, 0x83, 0x92, 0x4b, 0xa8, 0xfc, 0xab, 0xf4, 0x17, 0x98, 0xbb, 0xc9, 0xdf, 0x83, 0x55, 0xa1, 0x83, 0x8d, 0x3a, 0x47, 0xe6, 0xd9, 0x4d,
0x19, 0x2a, 0xfb, 0x4c, 0x7f, 0x81, 0x55, 0x1c, 0xf3, 0xbf, 0x09, 0xdd, 0x89, 0xd9, 0x5a, 0xf5, 0xe3, 0x1c, 0xe3, 0x2d, 0x9e, 0xe1, 0x6c, 0xf0, 0xdf, 0x03, 0x7f, 0x50, 0x72, 0x09, 0x95, 0x7f,
0xba, 0xa5, 0x36, 0xcd, 0x9c, 0x34, 0xa0, 0x1d, 0x58, 0xf8, 0x46, 0xbd, 0xd1, 0xc4, 0x7a, 0xa3, 0x35, 0x43, 0x65, 0x9f, 0xe9, 0x2f, 0xb0, 0x8a, 0x6d, 0xfe, 0x37, 0xa1, 0x3b, 0x31, 0x5b, 0xab,
0x48, 0xef, 0x9f, 0x79, 0xb0, 0x69, 0x74, 0xa4, 0x86, 0xf9, 0xcf, 0x9a, 0x43, 0x19, 0x85, 0xe5, 0x5e, 0xb7, 0xd4, 0xa6, 0x99, 0x93, 0x06, 0xb4, 0x03, 0x0b, 0xdf, 0xa8, 0x37, 0x9a, 0x58, 0x6f,
0xf5, 0xe2, 0xc8, 0x6b, 0xd6, 0x1c, 0xaa, 0xe4, 0x6c, 0xd6, 0x3f, 0x7c, 0x32, 0x97, 0xbd, 0xe9, 0x14, 0xe9, 0xfd, 0x33, 0x0f, 0x36, 0x8d, 0x8e, 0xd4, 0x30, 0xff, 0x59, 0x73, 0x28, 0xa3, 0xb0,
0x4e, 0xa2, 0xe6, 0x1f, 0xca, 0xf3, 0xd9, 0x7b, 0xd6, 0x20, 0xbd, 0xf0, 0xc9, 0x2f, 0x13, 0xfe, 0xbc, 0x5e, 0x1c, 0x79, 0xcd, 0x9a, 0x43, 0x95, 0x9c, 0xcd, 0xfa, 0xc2, 0x27, 0x73, 0xd9, 0x9b,
0x82, 0x1f, 0x35, 0x30, 0xf9, 0xd8, 0xb7, 0x51, 0xa5, 0x61, 0x97, 0x0a, 0x5e, 0xa9, 0x54, 0xd8, 0xee, 0x24, 0x6a, 0xfe, 0xa6, 0x3c, 0x9f, 0xbd, 0x67, 0x0d, 0xd2, 0x0b, 0x9f, 0xfc, 0x32, 0xe1,
0x86, 0x25, 0x5a, 0x49, 0x31, 0x2a, 0xa1, 0x9b, 0x20, 0xff, 0x65, 0x58, 0xc1, 0xf2, 0xe0, 0x28, 0x2f, 0xf8, 0x51, 0x03, 0x93, 0x8f, 0x7d, 0x1a, 0x55, 0x1a, 0x76, 0xa9, 0xe0, 0x95, 0x4a, 0x85,
0x1c, 0x73, 0x85, 0x24, 0x33, 0xa8, 0x03, 0x2d, 0x6a, 0xc7, 0x46, 0x75, 0xf7, 0xd7, 0x74, 0x7b, 0x6d, 0x58, 0xa2, 0x95, 0x14, 0xa3, 0x12, 0xba, 0x09, 0xf2, 0x5f, 0x86, 0x15, 0x2c, 0x0f, 0x8e,
0xe6, 0xa2, 0x2f, 0x6b, 0xcd, 0xeb, 0xcb, 0x16, 0xe7, 0xf4, 0x65, 0x6d, 0xa7, 0x2f, 0xb3, 0xfa, 0xc2, 0x31, 0x57, 0x48, 0x32, 0x83, 0x3a, 0xd0, 0xa2, 0x76, 0x6c, 0x54, 0x77, 0x7f, 0x4d, 0xb7,
0xc5, 0x8e, 0xdb, 0x2f, 0x1a, 0x5d, 0x1b, 0x5c, 0xd1, 0xb5, 0x2d, 0x5d, 0xa7, 0x6b, 0x5b, 0xae, 0x67, 0x2e, 0xfa, 0xb2, 0xd6, 0xbc, 0xbe, 0x6c, 0x71, 0x4e, 0x5f, 0xd6, 0x76, 0xfa, 0x32, 0xab,
0xe8, 0xda, 0x4a, 0x3d, 0x75, 0xf7, 0x9a, 0x3d, 0xf5, 0x4a, 0x75, 0x4f, 0xbd, 0x03, 0xab, 0xf4, 0x5f, 0xec, 0xb8, 0xfd, 0xa2, 0xd1, 0xb5, 0xc1, 0x15, 0x5d, 0xdb, 0xd2, 0x75, 0xba, 0xb6, 0xe5,
0x97, 0xe4, 0xfd, 0xa2, 0x7d, 0x59, 0x95, 0x98, 0x0e, 0x38, 0xf8, 0x7e, 0xd9, 0x37, 0x18, 0x1f, 0x8a, 0xae, 0xad, 0xd4, 0x53, 0x77, 0xaf, 0xd9, 0x53, 0xaf, 0x54, 0xf7, 0xd4, 0x3b, 0xb0, 0x4a,
0x24, 0xe9, 0xf0, 0x69, 0xf9, 0x46, 0xf0, 0x7f, 0xb0, 0x94, 0x6f, 0x1f, 0x5f, 0x50, 0x66, 0xbb, 0x9f, 0x24, 0xef, 0x17, 0xed, 0xcb, 0xaa, 0xc4, 0x74, 0xc0, 0xc1, 0xf7, 0xcb, 0xbe, 0xc1, 0xf8,
0xc8, 0xe7, 0x06, 0x98, 0xd9, 0x68, 0x25, 0xc7, 0x92, 0xc5, 0x8c, 0xf5, 0x18, 0xed, 0xc0, 0x9d, 0x20, 0x49, 0x87, 0x4f, 0xcb, 0x37, 0x82, 0xff, 0x83, 0xa5, 0xfc, 0xf5, 0xf1, 0x05, 0x65, 0xb6,
0x10, 0x5c, 0xe7, 0xef, 0xe2, 0xe0, 0x0f, 0x35, 0x58, 0xb7, 0x06, 0x9c, 0xff, 0x5b, 0x16, 0xdd, 0x8b, 0x7c, 0x6e, 0x80, 0x99, 0x8d, 0x56, 0x72, 0x2c, 0x59, 0xcc, 0x58, 0x8f, 0xd1, 0x0e, 0xdc,
0xf9, 0xb2, 0x16, 0xdd, 0x31, 0x2c, 0xba, 0x42, 0xff, 0x9d, 0x6a, 0xfd, 0xbf, 0x0b, 0xcf, 0x58, 0x09, 0xc1, 0x75, 0x3e, 0x17, 0x07, 0xbf, 0xaf, 0xc1, 0xba, 0x35, 0xe0, 0xfc, 0xdf, 0xb2, 0xe8,
0xc2, 0x22, 0xb9, 0x63, 0x40, 0x6b, 0x11, 0xdd, 0xee, 0xb4, 0xa8, 0x24, 0x58, 0xa6, 0xf0, 0x64, 0xce, 0x97, 0xb5, 0xe8, 0x8e, 0x61, 0xd1, 0x15, 0xfa, 0xef, 0x54, 0xeb, 0xff, 0x5d, 0x78, 0xc6,
0x60, 0x72, 0xf5, 0x87, 0x3c, 0x54, 0x6b, 0xaf, 0x34, 0xfd, 0xb2, 0xbe, 0x4c, 0xf9, 0x5b, 0x0d, 0x12, 0x16, 0xc9, 0x1d, 0x03, 0x5a, 0x8b, 0xe8, 0x76, 0xa7, 0x45, 0x25, 0xc1, 0x32, 0x85, 0x27,
0x56, 0x8a, 0x92, 0x06, 0xf3, 0x04, 0x9a, 0x23, 0x36, 0xe6, 0xda, 0x1c, 0xf1, 0x37, 0x85, 0xfc, 0x03, 0x93, 0xab, 0x3f, 0xe4, 0xa1, 0x5a, 0x7b, 0xa5, 0xe9, 0x97, 0xf5, 0xa3, 0xcb, 0x5f, 0x6b,
0x44, 0x57, 0xeb, 0x59, 0x82, 0x4a, 0x8e, 0xf2, 0xd4, 0x4d, 0xea, 0x69, 0x33, 0x03, 0x62, 0xd8, 0xb0, 0x52, 0x94, 0x34, 0x98, 0x27, 0xd0, 0x1c, 0xb1, 0x31, 0xd7, 0xe6, 0x88, 0xcf, 0x14, 0xf2,
0x5e, 0xc3, 0xb4, 0x3d, 0xa3, 0x0a, 0x6b, 0x5a, 0x55, 0x98, 0x0f, 0x0d, 0x6c, 0xf7, 0x95, 0x5e, 0x13, 0x5d, 0xad, 0x67, 0x09, 0x2a, 0x39, 0xca, 0x53, 0x37, 0xa9, 0xa7, 0xcd, 0x0c, 0x88, 0x61,
0xe8, 0x37, 0x75, 0x0e, 0xb2, 0x9c, 0x93, 0x7f, 0x53, 0xa9, 0x15, 0x32, 0x24, 0x19, 0xbf, 0x9c, 0x7b, 0x0d, 0xd3, 0xf6, 0x8c, 0x2a, 0xac, 0x69, 0x55, 0x61, 0x3e, 0x34, 0xb0, 0xdd, 0x57, 0x7a,
0x70, 0xd2, 0x47, 0x97, 0x15, 0x00, 0x43, 0xfd, 0x60, 0xa9, 0x9f, 0x3e, 0x03, 0x40, 0xb3, 0x41, 0xa1, 0x67, 0xea, 0x1c, 0x64, 0x39, 0x27, 0x3f, 0x53, 0xa9, 0x15, 0x32, 0x24, 0x19, 0xbf, 0x9c,
0x59, 0x2a, 0x4d, 0xdd, 0x20, 0x8c, 0x12, 0x9c, 0xfe, 0xe0, 0x0e, 0xd3, 0x50, 0x61, 0x6d, 0xca, 0x70, 0xd2, 0x47, 0x97, 0x15, 0x00, 0x43, 0xfd, 0x60, 0xa9, 0x9f, 0x7e, 0x03, 0x40, 0xb3, 0x41,
0x71, 0x44, 0x01, 0xc1, 0x40, 0x25, 0xa6, 0x83, 0x01, 0x17, 0xa2, 0xf7, 0x2c, 0xb1, 0xae, 0x97, 0x59, 0x2a, 0x4d, 0xdd, 0x20, 0x8c, 0x12, 0x9c, 0x3e, 0x70, 0x87, 0x69, 0xa8, 0xb0, 0x36, 0xe5,
0xc1, 0xdf, 0x3d, 0x39, 0xdd, 0xa5, 0xe1, 0xc3, 0xbd, 0x13, 0x8a, 0x14, 0x33, 0xe7, 0x92, 0xe6, 0x38, 0xa2, 0x80, 0x60, 0xa0, 0x12, 0xd3, 0xc1, 0x80, 0x0b, 0xd1, 0x7b, 0x96, 0x58, 0xd7, 0xcb,
0x64, 0xb1, 0xe6, 0x7c, 0xff, 0x72, 0xd5, 0x54, 0xf2, 0x65, 0x58, 0x99, 0x84, 0x98, 0xa7, 0x0e, 0xe0, 0x6f, 0x9e, 0x9c, 0xee, 0xd2, 0xf0, 0xe1, 0xde, 0x09, 0x45, 0x8a, 0x99, 0x73, 0x49, 0x73,
0xcd, 0xd9, 0xe4, 0x32, 0x73, 0xa0, 0x57, 0x0c, 0xd0, 0x5f, 0x82, 0x7a, 0x76, 0x21, 0x3f, 0x3b, 0xb2, 0x58, 0x73, 0xfe, 0x7f, 0xb9, 0x6a, 0x2a, 0xf9, 0x32, 0xac, 0x4c, 0x42, 0xcc, 0x53, 0x87,
0x59, 0xda, 0xf5, 0x95, 0xe5, 0x1d, 0x17, 0x1f, 0x4b, 0x31, 0xdc, 0x0e, 0xfe, 0xa2, 0x3e, 0x0e, 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, 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, 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, 0xeb, 0xbf, 0x94, 0x66, 0x31, 0x91, 0x77, 0x64, 0x35, 0xf3, 0xf7, 0x09, 0x1f, 0x1a, 0x63, 0x71,
0x26, 0xdb, 0xb4, 0x65, 0x46, 0xbf, 0x11, 0x13, 0xfb, 0x3b, 0xd1, 0x6b, 0x10, 0x50, 0x2e, 0x82, 0x26, 0xdb, 0xb4, 0x65, 0x46, 0xcf, 0x88, 0x89, 0xfd, 0x9d, 0xe8, 0x35, 0x08, 0x28, 0x17, 0xc1,
0xef, 0xc2, 0x73, 0x95, 0x0f, 0x1e, 0x9d, 0x27, 0x8f, 0x9f, 0xe0, 0xd1, 0x8e, 0x7c, 0x34, 0x38, 0x77, 0xe1, 0xb9, 0xca, 0x0b, 0x8f, 0xce, 0x93, 0xc7, 0x4f, 0x70, 0x69, 0x47, 0x5e, 0x1a, 0x9c,
0xd1, 0xa3, 0x67, 0x7d, 0x3d, 0x69, 0xe4, 0x0d, 0x68, 0x44, 0x45, 0x1b, 0xbb, 0x6d, 0x4d, 0x9e, 0xe8, 0xd1, 0xb3, 0x3e, 0x9e, 0x34, 0xf2, 0x06, 0x34, 0xa2, 0xa2, 0x8d, 0xdd, 0xb6, 0x26, 0xcf,
0x2b, 0xe8, 0x60, 0x84, 0x2d, 0x7b, 0xa0, 0x49, 0x34, 0xc8, 0xff, 0x60, 0x90, 0xab, 0x80, 0xc1, 0x15, 0x74, 0x30, 0xc2, 0x96, 0x3d, 0xd0, 0x24, 0x1a, 0xe4, 0x1f, 0x18, 0xe4, 0x2a, 0x60, 0xb0,
0xca, 0x03, 0x1e, 0x0e, 0x79, 0x7a, 0x74, 0x19, 0x0f, 0xf4, 0x90, 0xea, 0xe0, 0x9e, 0x1e, 0x8c, 0xf2, 0x80, 0x87, 0x43, 0x9e, 0x1e, 0x5d, 0xc6, 0x03, 0x3d, 0xa4, 0x3a, 0xb8, 0xa7, 0x07, 0x23,
0x1c, 0xdc, 0x43, 0x4f, 0x38, 0x09, 0x05, 0x3f, 0x18, 0x5e, 0xa8, 0x40, 0xae, 0x97, 0x78, 0x67, 0x07, 0xf7, 0xd0, 0x13, 0x4e, 0x42, 0xc1, 0x0f, 0x86, 0x17, 0x2a, 0x90, 0xeb, 0x25, 0x9e, 0x99,
0x72, 0x7a, 0x2a, 0xb8, 0x0e, 0xde, 0x6a, 0x15, 0xfc, 0xcc, 0x83, 0x2e, 0xd2, 0xf3, 0x70, 0xf7, 0x9c, 0x9e, 0x0a, 0xae, 0x83, 0xb7, 0x5a, 0x05, 0x3f, 0xf3, 0xa0, 0x8b, 0xf4, 0x3c, 0xdc, 0x7d,
0xe1, 0xd1, 0xf4, 0xe4, 0x50, 0x9c, 0xa9, 0x72, 0xd2, 0xd3, 0xe5, 0xa4, 0xff, 0x1a, 0xb4, 0x07, 0x78, 0x34, 0x3d, 0x39, 0x14, 0x67, 0xaa, 0x9c, 0xf4, 0x74, 0x39, 0xe9, 0xbf, 0x06, 0xed, 0x81,
0x6a, 0x78, 0xaa, 0x2a, 0xee, 0x0a, 0xcb, 0xc4, 0x76, 0x41, 0x63, 0xf9, 0x77, 0x61, 0x51, 0x5c, 0x1a, 0x9e, 0xaa, 0x8a, 0xbb, 0xc2, 0x32, 0xb1, 0x5d, 0xd0, 0x58, 0xfe, 0x5d, 0x58, 0x14, 0x97,
0xc6, 0x83, 0x43, 0x71, 0xe6, 0x8c, 0xb0, 0x6c, 0xea, 0xfb, 0x0b, 0x4c, 0xe3, 0x15, 0x35, 0xeb, 0xf1, 0xe0, 0x50, 0x9c, 0x39, 0x23, 0x2c, 0x9b, 0xfa, 0xfe, 0x02, 0xd3, 0x78, 0x45, 0xcd, 0xfa,
0x47, 0xb0, 0x72, 0x7f, 0x24, 0xe7, 0x09, 0x6a, 0xec, 0xbe, 0x05, 0xed, 0x48, 0xc8, 0x93, 0x44, 0x11, 0xac, 0xdc, 0x1f, 0xc9, 0x79, 0x82, 0x1a, 0xbb, 0x6f, 0x41, 0x3b, 0x12, 0x72, 0x27, 0x51,
0x55, 0x9b, 0xe5, 0x6b, 0xff, 0x55, 0x68, 0x8d, 0xe4, 0x4e, 0x6d, 0xce, 0x43, 0x4c, 0x21, 0x05, 0xd5, 0x66, 0xf9, 0xda, 0x7f, 0x15, 0x5a, 0x23, 0xf9, 0xa6, 0x36, 0xe7, 0x22, 0xa6, 0x90, 0x82,
0x2f, 0x42, 0x67, 0x4f, 0xff, 0xa7, 0x88, 0x36, 0xf9, 0x31, 0xbf, 0x54, 0xc2, 0xc3, 0x9f, 0xbb, 0x17, 0xa1, 0xb3, 0xa7, 0xbf, 0x29, 0xa2, 0x4d, 0x7e, 0xcc, 0x2f, 0x95, 0xf0, 0xf0, 0x71, 0xf7,
0x6f, 0x41, 0x27, 0xff, 0x62, 0xd1, 0xbf, 0x05, 0xad, 0x03, 0x81, 0x37, 0xf8, 0xdd, 0x3c, 0x05, 0x2d, 0xe8, 0xe4, 0x3f, 0x40, 0xfa, 0xb7, 0xa0, 0x75, 0x20, 0xf0, 0x04, 0xbf, 0x9b, 0xa7, 0x80,
0x3c, 0x7a, 0x3f, 0x1a, 0x6d, 0xad, 0xab, 0xe5, 0x81, 0xd8, 0x0f, 0xa7, 0x67, 0xe7, 0xd9, 0x87, 0x47, 0xef, 0x47, 0xa3, 0xad, 0x75, 0xb5, 0x3c, 0x10, 0xfb, 0xe1, 0xf4, 0xec, 0x3c, 0xfb, 0x70,
0x93, 0x60, 0xe1, 0xa4, 0x45, 0x9f, 0x29, 0xbe, 0xfe, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x72, 0x12, 0x2c, 0x9c, 0xb4, 0xe8, 0xaf, 0xc7, 0xd7, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x0e,
0x9f, 0xea, 0xe9, 0xf3, 0x28, 0x00, 0x00, 0x12, 0x6b, 0x42, 0x29, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // 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