Commit a98fbca6 authored by QM's avatar QM

updata

parent 9147f853
...@@ -212,9 +212,8 @@ func (b *blsClient) getSuperNodes() ([]string, string) { ...@@ -212,9 +212,8 @@ func (b *blsClient) getSuperNodes() ([]string, string) {
nodes, nodeStr := b.getSuperGroupNodes() nodes, nodeStr := b.getSuperGroupNodes()
if len(nodes) > 0 { if len(nodes) > 0 {
return nodes, nodeStr return nodes, nodeStr
} else {
return b.getSupervisionGroupNodes()
} }
return b.getSupervisionGroupNodes()
} else if b.typeNode == pt.ParaCommitSuperNode { } else if b.typeNode == pt.ParaCommitSuperNode {
return b.getSuperGroupNodes() return b.getSuperGroupNodes()
} else if b.typeNode == pt.ParaCommitSupervisionNode { } else if b.typeNode == pt.ParaCommitSupervisionNode {
...@@ -521,54 +520,42 @@ func (b *blsClient) blsSign(commits []*pt.ParacrossCommitAction) error { ...@@ -521,54 +520,42 @@ func (b *blsClient) blsSign(commits []*pt.ParacrossCommitAction) error {
} }
func (b *blsClient) getBlsPubKey(addr string) (crypto.PubKey, error) { func (b *blsClient) getBlsPubKey(addr string) (crypto.PubKey, error) {
if b.typeNode == pt.ParaCommitSuperNode || b.typeNode == pt.ParaCommitSupervisionNode { //先从缓存中获取
var funcName string if v, ok := b.peersBlsPubKey[addr]; ok {
if b.typeNode == pt.ParaCommitSuperNode { return v, nil
funcName = "GetNodeAddrInfo" }
} else {
funcName = "GetSupervisionNodeAddrInfo" //缓存没有,则从statedb获取
} cfg := b.paraClient.GetAPI().GetConfig()
ret, err := b.paraClient.GetAPI().QueryChain(&types.ChainExecutor{
//先从缓存中获取 Driver: "paracross",
if v, ok := b.peersBlsPubKey[addr]; ok { FuncName: "GetNodeAddrInfo",
return v, nil Param: types.Encode(&pt.ReqParacrossNodeInfo{Title: cfg.GetTitle(), Addr: addr}),
} })
if err != nil {
//缓存没有,则从statedb获取 plog.Error("commitmsg.GetNodeAddrInfo ", "err", err.Error())
cfg := b.paraClient.GetAPI().GetConfig() return nil, err
ret, err := b.paraClient.GetAPI().QueryChain(&types.ChainExecutor{ }
Driver: "paracross", resp, ok := ret.(*pt.ParaNodeAddrIdStatus)
FuncName: funcName, if !ok {
Param: types.Encode(&pt.ReqParacrossNodeInfo{Title: cfg.GetTitle(), Addr: addr}), plog.Error("commitmsg.getNodeGroupAddrs rsp nok")
}) return nil, err
if err != nil { }
plog.Error("commitmsg.GetNodeAddrInfo ", "funcName", funcName, "err", err.Error())
return nil, err
}
resp, ok := ret.(*pt.ParaNodeAddrIdStatus)
if !ok {
plog.Error("commitmsg.getNodeGroupAddrs rsp nok", "funcName", funcName)
return nil, err
}
s, err := common.FromHex(resp.BlsPubKey)
if err != nil {
plog.Error("commitmsg.getNode pubkey nok", "pubkey", resp.BlsPubKey, "funcName", funcName)
return nil, err
}
pubKey, err := b.cryptoCli.PubKeyFromBytes(s)
if err != nil {
plog.Error("verifyBlsSign.DeserializePublicKey", "key", addr)
return nil, err
}
plog.Info("getBlsPubKey", "addr", addr, "pub", resp.BlsPubKey, "serial", common.ToHex(pubKey.Bytes()))
b.peersBlsPubKey[addr] = pubKey
return pubKey, nil
s, err := common.FromHex(resp.BlsPubKey)
if err != nil {
plog.Error("commitmsg.getNode pubkey nok", "pubkey", resp.BlsPubKey)
return nil, err
}
pubKey, err := b.cryptoCli.PubKeyFromBytes(s)
if err != nil {
plog.Error("verifyBlsSign.DeserializePublicKey", "key", addr)
return nil, err
} }
plog.Info("getBlsPubKey", "addr", addr, "pub", resp.BlsPubKey, "serial", common.ToHex(pubKey.Bytes()))
b.peersBlsPubKey[addr] = pubKey
return nil, errors.New("b.typeNode = pt.ParaCommitNode") return pubKey, nil
} }
func (b *blsClient) verifyBlsSign(addr string, commit *pt.ParacrossCommitAction) error { func (b *blsClient) verifyBlsSign(addr string, commit *pt.ParacrossCommitAction) error {
......
...@@ -260,7 +260,7 @@ function para_configkey() { ...@@ -260,7 +260,7 @@ function para_configkey() {
function query_tx() { function query_tx() {
block_wait "${1}" 1 block_wait "${1}" 1
local times=200 local times=20
while true; do while true; do
ret=$(${1} tx query -s "${2}" | jq -r ".tx.hash") ret=$(${1} tx query -s "${2}" | jq -r ".tx.hash")
echo "query hash is ${2}, return ${ret} " echo "query hash is ${2}, return ${ret} "
...@@ -770,17 +770,17 @@ function para_create_supervision_nodegroup() { ...@@ -770,17 +770,17 @@ function para_create_supervision_nodegroup() {
check_balance_1ka "$balancePre" 6 check_balance_1ka "$balancePre" 6
echo "=========== # para chain quit supervision node group =============" echo "=========== # para chain cancel supervision node group ============="
balancePre=$(${CLI} account balance -a 1Ka7EPFRqs3v9yreXG6qA4RQbNmbPJCZPj -e paracross | jq -r ".frozen") balancePre=$(${CLI} account balance -a 1Ka7EPFRqs3v9yreXG6qA4RQbNmbPJCZPj -e paracross | jq -r ".frozen")
##quit ##cancel
txhash=$(${PARA_CLI} send para supervision_node quit -i "$id" -k 0xd165c84ed37c2a427fea487470ee671b7a0495d68d82607cafbc6348bf23bec5) txhash=$(${PARA_CLI} send para supervision_node cancel -i "$id" -k 0xd165c84ed37c2a427fea487470ee671b7a0495d68d82607cafbc6348bf23bec5)
echo "tx=$txhash" echo "tx=$txhash"
query_tx "${PARA_CLI}" "${txhash}" query_tx "${PARA_CLI}" "${txhash}"
newid=$(${PARA_CLI} para supervision_node list -s 3 | jq -r ".ids[0].id") newid=$(${PARA_CLI} para supervision_node list -s 4 | jq -r ".ids[0].id")
if [ -z "$newid" ]; then if [ -z "$newid" ]; then
${PARA_CLI} para supervision_node list -s 3 ${PARA_CLI} para supervision_node list -s 4
echo "quit status error " echo "cancel status error "
exit 1 # exit 1
fi fi
check_balance_1ka "$balancePre" -6 check_balance_1ka "$balancePre" -6
......
...@@ -237,7 +237,7 @@ function paracross_GetSupervisionInfo() { ...@@ -237,7 +237,7 @@ function paracross_GetSupervisionInfo() {
chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"GetSupervisionNodeGroupAddrs","payload":{"title":"user.p.para."}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("key","value"),true])' "GetSupervisionNodeGroupAddrs" chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"GetSupervisionNodeGroupAddrs","payload":{"title":"user.p.para."}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("key","value"),true])' "GetSupervisionNodeGroupAddrs"
chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"GetSupervisionNodeGroupStatus","payload":{"title":"user.p.para."}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "GetSupervisionNodeGroupStatus" chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"GetSupervisionNodeGroupStatus","payload":{"title":"user.p.para."}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "GetSupervisionNodeGroupStatus"
chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"ListSupervisionNodeGroupStatus","payload":{"title":"user.p.para.","status":2}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "ListSupervisionNodeGroupStatus status:2" chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"ListSupervisionNodeGroupStatus","payload":{"title":"user.p.para.","status":2}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "ListSupervisionNodeGroupStatus status:2"
chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"ListSupervisionNodeGroupStatus","payload":{"title":"user.p.para.","status":3}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "ListSupervisionNodeGroupStatus status:3" chain33_Http '{"method":"Chain33.Query","params":[{ "execer":"paracross", "funcName":"ListSupervisionNodeGroupStatus","payload":{"title":"user.p.para.","status":4}}]}' ${UNIT_HTTP} '(.error|not) and (.result| [has("status"),true])' "ListSupervisionNodeGroupStatus status:4"
} }
para_test_addr="1MAuE8QSbbech3bVKK2JPJJxYxNtT95oSU" para_test_addr="1MAuE8QSbbech3bVKK2JPJJxYxNtT95oSU"
......
...@@ -1522,6 +1522,7 @@ func supervisionNodeCmd() *cobra.Command { ...@@ -1522,6 +1522,7 @@ func supervisionNodeCmd() *cobra.Command {
cmd.AddCommand(supervisionNodeApplyCmd()) cmd.AddCommand(supervisionNodeApplyCmd())
cmd.AddCommand(supervisionNodeApproveCmd()) cmd.AddCommand(supervisionNodeApproveCmd())
cmd.AddCommand(supervisionNodeQuitCmd()) cmd.AddCommand(supervisionNodeQuitCmd())
cmd.AddCommand(supervisionNodeCancelCmd())
cmd.AddCommand(getSupervisionNodeGroupAddrsCmd()) cmd.AddCommand(getSupervisionNodeGroupAddrsCmd())
cmd.AddCommand(supervisionNodeGroupStatusCmd()) cmd.AddCommand(supervisionNodeGroupStatusCmd())
...@@ -1623,18 +1624,52 @@ func supervisionNodeQuitCmd() *cobra.Command { ...@@ -1623,18 +1624,52 @@ func supervisionNodeQuitCmd() *cobra.Command {
} }
func addSupervisionNodeQuitCmdFlags(cmd *cobra.Command) { func addSupervisionNodeQuitCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("id", "i", "", "apply quit id for supervision node") cmd.Flags().StringP("addr", "a", "", "apply quit id for supervision node")
_ = cmd.MarkFlagRequired("id") _ = cmd.MarkFlagRequired("addr")
} }
func supervisionNodeQuit(cmd *cobra.Command, args []string) { func supervisionNodeQuit(cmd *cobra.Command, args []string) {
paraName, _ := cmd.Flags().GetString("paraName") paraName, _ := cmd.Flags().GetString("paraName")
opAddr, _ := cmd.Flags().GetString("addr")
if !strings.HasPrefix(paraName, "user.p") {
_, _ = fmt.Fprintln(os.Stderr, "paraName is not right, paraName format like `user.p.guodun.`")
return
}
payload := &pt.ParaNodeAddrConfig{Title: paraName, Op: 3, Addr: opAddr}
params := &rpctypes.CreateTxIn{
Execer: getRealExecName(paraName, pt.ParaX),
ActionName: "SupervisionNodeGroupConfig",
Payload: types.MustPBToJSON(payload),
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
}
func supervisionNodeCancelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "cancel",
Short: "cancel for para chain's supervision node application",
Run: supervisionNodeCancel,
}
addSupervisionNodeCancelCmdFlags(cmd)
return cmd
}
func addSupervisionNodeCancelCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("id", "i", "", "apply cancel id for supervision node")
_ = cmd.MarkFlagRequired("id")
}
func supervisionNodeCancel(cmd *cobra.Command, args []string) {
paraName, _ := cmd.Flags().GetString("paraName")
id, _ := cmd.Flags().GetString("id") id, _ := cmd.Flags().GetString("id")
if !strings.HasPrefix(paraName, "user.p") { if !strings.HasPrefix(paraName, "user.p") {
_, _ = fmt.Fprintln(os.Stderr, "paraName is not right, paraName format like `user.p.guodun.`") _, _ = fmt.Fprintln(os.Stderr, "paraName is not right, paraName format like `user.p.guodun.`")
return return
} }
payload := &pt.ParaNodeAddrConfig{Title: paraName, Op: 3, Id: id} payload := &pt.ParaNodeAddrConfig{Title: paraName, Op: 4, Id: id}
params := &rpctypes.CreateTxIn{ params := &rpctypes.CreateTxIn{
Execer: getRealExecName(paraName, pt.ParaX), Execer: getRealExecName(paraName, pt.ParaX),
ActionName: "SupervisionNodeGroupConfig", ActionName: "SupervisionNodeGroupConfig",
......
...@@ -452,30 +452,20 @@ func getValidAddrs(nodes map[string]struct{}, addrs []string) []string { ...@@ -452,30 +452,20 @@ func getValidAddrs(nodes map[string]struct{}, addrs []string) []string {
} }
//get secp256 addr's bls pubkey //get secp256 addr's bls pubkey
func getAddrBlsPubKey(db dbm.KV, title, addr string, commitNodeType uint32) (string, error) { func getAddrBlsPubKey(db dbm.KV, title, addr string) (string, error) {
if commitNodeType == pt.ParaCommitSuperNode { addrStat, err := getNodeAddr(db, title, addr)
addrStat, err := getNodeAddr(db, title, addr) if err != nil {
if err != nil { return "", errors.Wrapf(err, "nodeAddr:%s-%s get error", title, addr)
return "", errors.Wrapf(err, "nodeAddr:%s-%s get error", title, addr)
}
return addrStat.BlsPubKey, nil
} else if commitNodeType == pt.ParaCommitSupervisionNode {
addrStat, err := getSupervisionNodeAddr(db, title, addr)
if err != nil {
return "", errors.Wrapf(err, "Supervision nodeAddr:%s-%s get error", title, addr)
}
return addrStat.BlsPubKey, nil
} else {
return "", errors.New("commitNodeType is ParaCommitNode")
} }
return addrStat.BlsPubKey, nil
} }
//bls签名共识交易验证 大约平均耗时3ms (2~4ms) //bls签名共识交易验证 大约平均耗时3ms (2~4ms)
func (a *action) procBlsSign(nodesArry []string, commit *pt.ParacrossCommitAction, commitNodeType uint32) ([]string, error) { func (a *action) procBlsSign(nodesArry []string, commit *pt.ParacrossCommitAction) ([]string, error) {
signAddrs := util.GetAddrsByBitMap(nodesArry, commit.Bls.AddrsMap) signAddrs := util.GetAddrsByBitMap(nodesArry, commit.Bls.AddrsMap)
var pubs []string var pubs []string
for _, addr := range signAddrs { for _, addr := range signAddrs {
pub, err := getAddrBlsPubKey(a.db, commit.Status.Title, addr, commitNodeType) pub, err := getAddrBlsPubKey(a.db, commit.Status.Title, addr /*, commitNodeType*/)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "pubkey not exist to addr=%s", addr) return nil, errors.Wrapf(err, "pubkey not exist to addr=%s", addr)
} }
...@@ -529,6 +519,25 @@ func verifyBlsSign(cryptoCli crypto.Crypto, pubs []string, commit *pt.ParacrossC ...@@ -529,6 +519,25 @@ func verifyBlsSign(cryptoCli crypto.Crypto, pubs []string, commit *pt.ParacrossC
return nil return nil
} }
func (a *action) getValidCommitAddrs(commit *pt.ParacrossCommitAction, nodesMap map[string]struct{}, nodesArry []string) ([]string, error) {
//获取commitAddrs, bls sign 包含多个账户的聚合签名
commitAddrs := []string{a.fromaddr}
if commit.Bls != nil {
addrs, err := a.procBlsSign(nodesArry, commit)
if err != nil {
return nil, errors.Wrap(err, "procBlsSign")
}
commitAddrs = addrs
}
validAddrs := getValidAddrs(nodesMap, commitAddrs)
if len(validAddrs) <= 0 {
return nil, errors.Wrapf(errors.New("getValidAddrs error"), "getValidAddrs nil commitAddrs=%s ", strings.Join(commitAddrs, ","))
}
return validAddrs, nil
}
//共识commit msg 处理 //共识commit msg 处理
func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error) { func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error) {
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
...@@ -545,31 +554,17 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error ...@@ -545,31 +554,17 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error
return nil, errors.Wrap(err, "getNodesGroup") return nil, errors.Wrap(err, "getNodesGroup")
} }
var commitAddrs, commitSupervisionAddrs, validAddrs, supervisionValidAddrs []string var validAddrs, supervisionValidAddrs []string
bIsCommitSuperNode := false bIsCommitSuperNode := false
bIsCommitSupervisionNode := false bIsCommitSupervisionNode := false
for _, addr := range nodesArry { if _, exist := nodesMap[a.fromaddr]; exist {
if addr == a.fromaddr { validAddrs, err = a.getValidCommitAddrs(commit, nodesMap, nodesArry)
// 授权节点共识 if err != nil {
//获取commitAddrs, bls sign 包含多个账户的聚合签名 return nil, errors.Wrap(err, "getValidCommitAddrs")
commitAddrs = []string{a.fromaddr}
if commit.Bls != nil {
addrs, err := a.procBlsSign(nodesArry, commit, pt.ParaCommitSuperNode)
if err != nil {
return nil, errors.Wrap(err, "procBlsSign")
}
commitAddrs = addrs
}
validAddrs = getValidAddrs(nodesMap, commitAddrs)
if len(validAddrs) <= 0 {
return nil, errors.Wrapf(err, "getValidAddrs nil commitAddrs=%s ", strings.Join(commitAddrs, ","))
}
bIsCommitSuperNode = true
break
} }
bIsCommitSuperNode = true
} }
// 获取监督节点的数据 监督节点在高度分叉后 // 获取监督节点的数据 监督节点在高度分叉后
...@@ -579,27 +574,12 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error ...@@ -579,27 +574,12 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error
} }
if !bIsCommitSuperNode { if !bIsCommitSuperNode {
for _, addr := range supervisionNodesArry { if _, exist := supervisionNodesMap[a.fromaddr]; exist {
if addr == a.fromaddr { supervisionValidAddrs, err = a.getValidCommitAddrs(commit, supervisionNodesMap, supervisionNodesArry)
// 监督节点共识 if err != nil {
//获取commitAddrs, bls sign 包含多个账户的聚合签名 return nil, errors.Wrap(err, "getValidCommitAddrs")
commitSupervisionAddrs = []string{a.fromaddr}
if commit.Bls != nil {
addrs, err := a.procBlsSign(supervisionNodesArry, commit, pt.ParaCommitSupervisionNode)
if err != nil {
return nil, errors.Wrap(err, "procBlsSign")
}
commitSupervisionAddrs = addrs
}
supervisionValidAddrs = getValidAddrs(supervisionNodesMap, commitSupervisionAddrs)
if len(supervisionValidAddrs) <= 0 {
return nil, errors.Wrapf(err, "getValidAddrs nil commitSupervisionAddrs=%s", strings.Join(commitSupervisionAddrs, ","))
}
bIsCommitSupervisionNode = true
break
} }
bIsCommitSupervisionNode = true
} }
} }
...@@ -646,16 +626,18 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -646,16 +626,18 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s
var copyStat *pt.ParacrossHeightStatus var copyStat *pt.ParacrossHeightStatus
if isNotFound(err) { if isNotFound(err) {
stat = &pt.ParacrossHeightStatus{ stat = &pt.ParacrossHeightStatus{
Status: pt.ParacrossStatusCommiting, Status: pt.ParacrossStatusCommiting,
Title: commit.Title, Title: commit.Title,
Height: commit.Height, Height: commit.Height,
Details: &pt.ParacrossStatusDetails{}, Details: &pt.ParacrossStatusDetails{},
SupervisionDetails: &pt.ParacrossStatusDetails{},
} }
if pt.IsParaForkHeight(cfg, a.exec.GetMainHeight(), pt.ForkCommitTx) { if pt.IsParaForkHeight(cfg, a.exec.GetMainHeight(), pt.ForkCommitTx) {
stat.MainHeight = commit.MainBlockHeight stat.MainHeight = commit.MainBlockHeight
stat.MainHash = commit.MainBlockHash stat.MainHash = commit.MainBlockHash
} }
if pt.IsParaForkHeight(cfg, a.exec.GetMainHeight(), pt.ForkParaSupervision) {
stat.SupervisionDetails = &pt.ParacrossStatusDetails{}
}
} else { } else {
copyStat = proto.Clone(stat).(*pt.ParacrossHeightStatus) copyStat = proto.Clone(stat).(*pt.ParacrossHeightStatus)
} }
...@@ -692,7 +674,7 @@ func (a *action) proCommitMsg(commit *pt.ParacrossNodeStatus, nodes map[string]s ...@@ -692,7 +674,7 @@ 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) //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更新而更新
...@@ -736,6 +718,7 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac ...@@ -736,6 +718,7 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac
return receipt, nil return receipt, nil
} }
clog.Debug("paracross.Commit commit ----pass", "most", mostCount, "mostHash", common.ToHex([]byte(mostHash)))
// 如果已经有监督节点 // 如果已经有监督节点
if len(supervisionNodes) > 0 { if len(supervisionNodes) > 0 {
for i, v := range stat.SupervisionDetails.Addrs { for i, v := range stat.SupervisionDetails.Addrs {
...@@ -746,9 +729,14 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac ...@@ -746,9 +729,14 @@ func (a *action) commitTxDone(nodeStatus *pt.ParacrossNodeStatus, stat *pt.Parac
return receipt, nil return receipt, nil
} }
clog.Debug("paracross.Commit commit SupervisionDetails ----pass", "mostSupervisionCount", mostSupervisionCount, "mostSupervisionHash", common.ToHex([]byte(mostSupervisionHash))) clog.Debug("paracross.Commit commit SupervisionDetails ----pass", "mostSupervisionCount", mostSupervisionCount, "mostSupervisionHash", common.ToHex([]byte(mostSupervisionHash)))
if common.ToHex([]byte(mostHash)) != common.ToHex([]byte(mostSupervisionHash)) {
clog.Debug("paracross.Commit commit mostSupervisionHash mostHash not equal")
return receipt, nil
}
} }
clog.Debug("paracross.Commit commit ----pass")
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)
...@@ -929,7 +917,7 @@ func (a *action) commitTxDoneByStat(stat *pt.ParacrossHeightStatus, titleStatus ...@@ -929,7 +917,7 @@ func (a *action) commitTxDoneByStat(stat *pt.ParacrossHeightStatus, titleStatus
} }
clog.Debug("paracross.commitTxDoneByStat ----pass", "most", most, "mostHash", common.ToHex([]byte(mostHash))) clog.Debug("paracross.commitTxDoneByStat ----pass", "most", most, "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)
r := makeCommitStatReceipt(stat) r := makeCommitStatReceipt(stat)
receipt = mergeReceipt(receipt, r) receipt = mergeReceipt(receipt, r)
......
...@@ -40,12 +40,10 @@ var ( ...@@ -40,12 +40,10 @@ var (
paraBindMinderNode string paraBindMinderNode string
//监督节点 //监督节点
paraSupervisionNodes string paraSupervisionNodes string
paraSupervisionNodeAddr string paraSupervisionNodeGroupStatusAddrs string
paraSupervisionNodeGroupStatusAddrs string paraSupervisionNodeGroupIDPrefix string
paraSupervisionNodeGroupIDPrefix string paraSupervisionSelfConsensStages string
paraSupervisionSelfConsensStages string
paraSupervisionSelfConsensStageIDPrefix string
localSupervisionNodeGroupStatusTitle string localSupervisionNodeGroupStatusTitle string
) )
...@@ -78,11 +76,9 @@ func setPrefix() { ...@@ -78,11 +76,9 @@ func setPrefix() {
localNodeGroupStatusTitle = "LODB-paracross-nodegroupStatusTitle-" localNodeGroupStatusTitle = "LODB-paracross-nodegroupStatusTitle-"
paraSupervisionNodes = "mavl-paracross-supervision-nodes-title-" paraSupervisionNodes = "mavl-paracross-supervision-nodes-title-"
paraSupervisionNodeAddr = "mavl-paracross-supervision-nodes-titleAddr-"
paraSupervisionNodeGroupStatusAddrs = "mavl-paracross-supervision-nodegroup-apply-title-" paraSupervisionNodeGroupStatusAddrs = "mavl-paracross-supervision-nodegroup-apply-title-"
paraSupervisionNodeGroupIDPrefix = "mavl-paracross-title-nodegroupid-supervision-" paraSupervisionNodeGroupIDPrefix = "mavl-paracross-title-nodegroupid-supervision-"
paraSupervisionSelfConsensStages = "mavl-paracross-supervision-selfconsens-stages-" paraSupervisionSelfConsensStages = "mavl-paracross-supervision-selfconsens-stages-"
paraSupervisionSelfConsensStageIDPrefix = "mavl-paracross-selfconsens-id-supervision-"
localSupervisionNodeGroupStatusTitle = "LODB-paracross-supervision-nodegroupStatusTitle-" localSupervisionNodeGroupStatusTitle = "LODB-paracross-supervision-nodegroupStatusTitle-"
} }
...@@ -234,10 +230,6 @@ func calcParaSupervisionNodeGroupStatusKey(title string) []byte { ...@@ -234,10 +230,6 @@ func calcParaSupervisionNodeGroupStatusKey(title string) []byte {
return []byte(fmt.Sprintf(paraSupervisionNodeGroupStatusAddrs+"%s", title)) 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 { func calcParaSupervisionNodeGroupIDKey(title, hash string) string {
return fmt.Sprintf(paraSupervisionNodeGroupIDPrefix+"%s-%s", title, hash) return fmt.Sprintf(paraSupervisionNodeGroupIDPrefix+"%s-%s", title, hash)
} }
...@@ -295,7 +295,9 @@ func checkCommitReceipt(suite *CommitTestSuite, receipt *types.Receipt, commitCn ...@@ -295,7 +295,9 @@ func checkCommitReceipt(suite *CommitTestSuite, receipt *types.Receipt, commitCn
assert.Equal(suite.T(), int32(pt.ParacrossStatusCommiting), titleHeight.Status) assert.Equal(suite.T(), int32(pt.ParacrossStatusCommiting), titleHeight.Status)
assert.Equal(suite.T(), Title, titleHeight.Title) assert.Equal(suite.T(), Title, titleHeight.Title)
assert.Equal(suite.T(), commitCnt, len(titleHeight.Details.Addrs)) assert.Equal(suite.T(), commitCnt, len(titleHeight.Details.Addrs))
assert.Equal(suite.T(), commitSupervisionCnt, len(titleHeight.SupervisionDetails.Addrs)) if commitSupervisionCnt > 0 {
assert.Equal(suite.T(), commitSupervisionCnt, len(titleHeight.SupervisionDetails.Addrs))
}
} }
func checkDoneReceipt(suite suite.Suite, receipt *types.Receipt, commitCnt int) { func checkDoneReceipt(suite suite.Suite, receipt *types.Receipt, commitCnt int) {
...@@ -375,7 +377,6 @@ func (suite *CommitTestSuite) TestExec() { ...@@ -375,7 +377,6 @@ func (suite *CommitTestSuite) TestExec() {
receipt = commitOnce(suite, PrivKeyD) receipt = commitOnce(suite, PrivKeyD)
checkRecordReceipt(suite, receipt, 4) checkRecordReceipt(suite, receipt, 4)
} }
func TestCommitSuite(t *testing.T) { func TestCommitSuite(t *testing.T) {
......
...@@ -48,6 +48,10 @@ func (p *Paracross) Query_GetTitleHeight(in *pt.ReqParacrossTitleHeight) (types. ...@@ -48,6 +48,10 @@ func (p *Paracross) Query_GetTitleHeight(in *pt.ReqParacrossTitleHeight) (types.
res.CommitAddrs = append(res.CommitAddrs, addr) res.CommitAddrs = append(res.CommitAddrs, addr)
res.CommitBlockHash = append(res.CommitBlockHash, common.ToHex(status.Details.BlockHash[i])) res.CommitBlockHash = append(res.CommitBlockHash, common.ToHex(status.Details.BlockHash[i]))
} }
for i, addr := range status.SupervisionDetails.Addrs {
res.CommitSupervisionAddrs = append(res.CommitSupervisionAddrs, addr)
res.CommitSupervisionBlockHash = append(res.CommitSupervisionBlockHash, common.ToHex(status.SupervisionDetails.BlockHash[i]))
}
return res, nil return res, nil
} }
...@@ -154,29 +158,6 @@ func (p *Paracross) Query_GetNodeAddrInfo(in *pt.ReqParacrossNodeInfo) (types.Me ...@@ -154,29 +158,6 @@ func (p *Paracross) Query_GetNodeAddrInfo(in *pt.ReqParacrossNodeInfo) (types.Me
return stat, nil return stat, nil
} }
//Query_GetSupervisionNodeAddrInfo get specific node addr info
func (p *Paracross) Query_GetSupervisionNodeAddrInfo(in *pt.ReqParacrossNodeInfo) (types.Message, error) {
if in == nil || in.Addr == "" {
return nil, types.ErrInvalidParam
}
cfg := p.GetAPI().GetConfig()
if cfg.IsPara() {
in.Title = cfg.GetTitle()
} else if in.Title == "" {
return nil, types.ErrInvalidParam
}
stat, err := getSupervisionNodeAddr(p.GetStateDB(), in.Title, in.Addr)
if err != nil {
return nil, err
}
stat.QuitId = getParaNodeIDSuffix(stat.QuitId)
stat.ProposalId = getParaNodeIDSuffix(stat.ProposalId)
return stat, nil
}
func (p *Paracross) getMainHeight() (int64, error) { func (p *Paracross) getMainHeight() (int64, error) {
mainHeight := p.GetMainHeight() mainHeight := p.GetMainHeight()
cfg := p.GetAPI().GetConfig() cfg := p.GetAPI().GetConfig()
......
...@@ -445,7 +445,6 @@ func updateVotes(in *pt.ParaNodeVoteDetail, nodes map[string]struct{}) *pt.ParaN ...@@ -445,7 +445,6 @@ func updateVotes(in *pt.ParaNodeVoteDetail, nodes map[string]struct{}) *pt.ParaN
//由于propasal id 和quit id分开,quit id不知道对应addr proposal id的coinfrozen信息,需要维护一个围绕addr的数据库结构信息 //由于propasal id 和quit id分开,quit id不知道对应addr proposal id的coinfrozen信息,需要维护一个围绕addr的数据库结构信息
func (a *action) updateNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt, error) { func (a *action) updateNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt, error) {
cfg := a.api.GetConfig()
addrStat, err := getNodeAddr(a.db, stat.Title, stat.TargetAddr) addrStat, err := getNodeAddr(a.db, stat.Title, stat.TargetAddr)
if err != nil { if err != nil {
if !isNotFound(err) { if !isNotFound(err) {
...@@ -479,6 +478,7 @@ func (a *action) updateNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt ...@@ -479,6 +478,7 @@ func (a *action) updateNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt
addrStat.QuitId = stat.Id addrStat.QuitId = stat.Id
receipt := makeParaNodeStatusReceipt(a.fromaddr, &preStat, addrStat) receipt := makeParaNodeStatusReceipt(a.fromaddr, &preStat, addrStat)
cfg := a.api.GetConfig()
if !cfg.IsPara() { if !cfg.IsPara() {
r, err := a.nodeGroupCoinsActive(proposalStat.FromAddr, proposalStat.CoinsFrozen, 1) r, err := a.nodeGroupCoinsActive(proposalStat.FromAddr, proposalStat.CoinsFrozen, 1)
if err != nil { if err != nil {
...@@ -592,7 +592,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -592,7 +592,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
} }
} else { } else {
if stat.Status == pt.ParaApplyJoining { if stat.Status == pt.ParaApplyJoining {
r, err := unpdateNodeGroup(a.db, config.Title, stat.TargetAddr, true) r, err := updateNodeGroup(a.db, config.Title, stat.TargetAddr, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -607,7 +607,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -607,7 +607,7 @@ 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 if stat.Status == pt.ParaApplyQuiting { } else if stat.Status == pt.ParaApplyQuiting {
r, err := unpdateNodeGroup(a.db, config.Title, stat.TargetAddr, false) r, err := updateNodeGroup(a.db, config.Title, stat.TargetAddr, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -623,7 +623,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -623,7 +623,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
//node quit后,如果committx满足2/3目标,自动触发commitDone //node quit后,如果committx满足2/3目标,自动触发commitDone
r, err = a.loopCommitTxDone(config.Title) r, err = a.loopCommitTxDone(config.Title)
if err != nil { if err != nil {
clog.Error("unpdateNodeGroup.loopCommitTxDone", "title", title, "err", err.Error()) clog.Error("updateNodeGroup.loopCommitTxDone", "title", title, "err", err.Error())
} }
receipt = mergeReceipt(receipt, r) receipt = mergeReceipt(receipt, r)
} }
...@@ -640,7 +640,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) ...@@ -640,7 +640,7 @@ func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error)
return receipt, nil return receipt, nil
} }
func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) { func updateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) {
var item types.ConfigItem var item types.ConfigItem
key := calcParaNodeGroupAddrsKey(title) key := calcParaNodeGroupAddrsKey(title)
...@@ -651,7 +651,7 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, ...@@ -651,7 +651,7 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt,
if value != nil { if value != nil {
err = types.Decode(value, &item) err = types.Decode(value, &item)
if err != nil { if err != nil {
clog.Error("unpdateNodeGroup", "decode db key", key) clog.Error("updateNodeGroup", "decode db key", key)
return nil, err // types.ErrBadConfigValue return nil, err // types.ErrBadConfigValue
} }
} }
...@@ -663,8 +663,7 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, ...@@ -663,8 +663,7 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt,
if add { if add {
item.GetArr().Value = append(item.GetArr().Value, addr) item.GetArr().Value = append(item.GetArr().Value, addr)
item.Addr = addr item.Addr = addr
clog.Info("unpdateNodeGroup add", "addr", addr, "from", copyItem.GetArr().Value, "to", item.GetArr().Value) clog.Info("updateNodeGroup add", "addr", addr, "from", copyItem.GetArr().Value, "to", item.GetArr().Value)
} else { } else {
//必须保留至少1个授权账户 //必须保留至少1个授权账户
if len(item.GetArr().Value) <= 1 { if len(item.GetArr().Value) <= 1 {
...@@ -677,11 +676,11 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, ...@@ -677,11 +676,11 @@ func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt,
item.GetArr().Value = append(item.GetArr().Value, value) item.GetArr().Value = append(item.GetArr().Value, value)
} }
} }
clog.Info("unpdateNodeGroup delete", "addr", addr) clog.Info("updateNodeGroup delete", "addr", addr)
} }
err = db.Set(key, types.Encode(&item)) err = db.Set(key, types.Encode(&item))
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "unpdateNodeGroup set dbkey=%s", key) return nil, errors.Wrapf(err, "updateNodeGroup set dbkey=%s", key)
} }
return makeParaNodeGroupReceipt(title, &copyItem, &item), nil return makeParaNodeGroupReceipt(title, &copyItem, &item), nil
} }
......
...@@ -2,17 +2,16 @@ package executor ...@@ -2,17 +2,16 @@ package executor
import ( import (
"fmt" "fmt"
"strconv"
"strings"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db" dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types" pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func makeSupervisionNodeGroupIDReceipt(addr string, prev, current *pt.ParaNodeGroupStatus) *types.Receipt { func makeSupervisionNodeIDReceipt(addr string, prev, current *pt.ParaNodeGroupStatus) *types.Receipt {
log := &pt.ReceiptParaNodeGroupConfig{ log := &pt.ReceiptParaNodeGroupConfig{
Addr: addr, Addr: addr,
Prev: prev, Prev: prev,
...@@ -71,7 +70,7 @@ func makeSupervisionNodeConfigReceipt(fromAddr string, config *pt.ParaNodeAddrCo ...@@ -71,7 +70,7 @@ func makeSupervisionNodeConfigReceipt(fromAddr string, config *pt.ParaNodeAddrCo
} }
func makeParaSupervisionNodeStatusReceipt(fromAddr string, prev, current *pt.ParaNodeAddrIdStatus) *types.Receipt { func makeParaSupervisionNodeStatusReceipt(fromAddr string, prev, current *pt.ParaNodeAddrIdStatus) *types.Receipt {
key := calcParaSupervisionNodeAddrKey(current.Title, current.Addr) key := calcParaNodeAddrKey(current.Title, current.Addr)
log := &pt.ReceiptParaNodeAddrStatUpdate{ log := &pt.ReceiptParaNodeAddrStatUpdate{
FromAddr: fromAddr, FromAddr: fromAddr,
Prev: prev, Prev: prev,
...@@ -141,17 +140,17 @@ func getSupervisionNodeGroupStatus(db dbm.KV, title string) (*pt.ParaNodeGroupSt ...@@ -141,17 +140,17 @@ func getSupervisionNodeGroupStatus(db dbm.KV, title string) (*pt.ParaNodeGroupSt
return &status, err return &status, err
} }
func getSupervisionNodeAddr(db dbm.KV, title, addr string) (*pt.ParaNodeAddrIdStatus, error) { //func getSupervisionNodeAddr(db dbm.KV, title, addr string) (*pt.ParaNodeAddrIdStatus, error) {
key := calcParaSupervisionNodeAddrKey(title, addr) // key := calcParaSupervisionNodeAddrKey(title, addr)
val, err := db.Get(key) // val, err := db.Get(key)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
//
var status pt.ParaNodeAddrIdStatus // var status pt.ParaNodeAddrIdStatus
err = types.Decode(val, &status) // err = types.Decode(val, &status)
return &status, err // return &status, err
} //}
func supervisionSelfConsentInitStage(cfg *types.Chain33Config) *types.Receipt { func supervisionSelfConsentInitStage(cfg *types.Chain33Config) *types.Receipt {
isEnable := cfg.IsEnable(pt.ParaConsSubConf + "." + pt.ParaSelfConsInitConf) isEnable := cfg.IsEnable(pt.ParaConsSubConf + "." + pt.ParaSelfConsInitConf)
...@@ -163,7 +162,7 @@ func supervisionSelfConsentInitStage(cfg *types.Chain33Config) *types.Receipt { ...@@ -163,7 +162,7 @@ func supervisionSelfConsentInitStage(cfg *types.Chain33Config) *types.Receipt {
return makeStageSupervisionGroupReceipt(nil, stages) return makeStageSupervisionGroupReceipt(nil, stages)
} }
func getSupervisionNodeGroupID(cfg *types.Chain33Config, db dbm.KV, title string, height int64, id string) (*pt.ParaNodeGroupStatus, error) { func getSupervisionNodeGroupID(cfg *types.Chain33Config, db dbm.KV, title string, height int64, id string) (*pt.ParaNodeIdStatus, error) {
if pt.IsParaForkHeight(cfg, height, pt.ForkLoopCheckCommitTxDone) { if pt.IsParaForkHeight(cfg, height, pt.ForkLoopCheckCommitTxDone) {
id = calcParaSupervisionNodeGroupIDKey(title, id) id = calcParaSupervisionNodeGroupIDKey(title, id)
} }
...@@ -172,11 +171,52 @@ func getSupervisionNodeGroupID(cfg *types.Chain33Config, db dbm.KV, title string ...@@ -172,11 +171,52 @@ func getSupervisionNodeGroupID(cfg *types.Chain33Config, db dbm.KV, title string
return nil, err return nil, err
} }
var status pt.ParaNodeGroupStatus var status pt.ParaNodeIdStatus
err = types.Decode(val, &status) err = types.Decode(val, &status)
return &status, err return &status, err
} }
func updateSupervisionNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) {
var item types.ConfigItem
key := calcParaSupervisionNodeGroupAddrsKey(title)
value, err := db.Get(key)
if err != nil {
return nil, err
}
if value != nil {
err = types.Decode(value, &item)
if err != nil {
clog.Error("updateSupervisionNodeGroup", "decode db key", key)
return nil, err // types.ErrBadConfigValue
}
}
copyValue := *item.GetArr()
copyItem := item
copyItem.Value = &types.ConfigItem_Arr{Arr: &copyValue}
if add {
item.GetArr().Value = append(item.GetArr().Value, addr)
item.Addr = addr
clog.Info("updateSupervisionNodeGroup add", "addr", addr, "from", copyItem.GetArr().Value, "to", item.GetArr().Value)
} else {
item.Addr = addr
item.GetArr().Value = make([]string, 0)
for _, value := range copyItem.GetArr().Value {
if value != addr {
item.GetArr().Value = append(item.GetArr().Value, value)
}
}
clog.Info("updateSupervisionNodeGroup delete", "addr", addr)
}
err = db.Set(key, types.Encode(&item))
if err != nil {
return nil, errors.Wrapf(err, "updateNodeGroup set dbkey=%s", key)
}
return makeParaSupervisionNodeGroupReceipt(title, &copyItem, &item), nil
}
func (a *action) checkValidSupervisionNode(config *pt.ParaNodeAddrConfig) (bool, error) { func (a *action) checkValidSupervisionNode(config *pt.ParaNodeAddrConfig) (bool, error) {
nodes, _, err := getParacrossSupervisonNodes(a.db, config.Title) nodes, _, err := getParacrossSupervisonNodes(a.db, config.Title)
if err != nil && !isNotFound(err) { if err != nil && !isNotFound(err) {
...@@ -189,69 +229,60 @@ func (a *action) checkValidSupervisionNode(config *pt.ParaNodeAddrConfig) (bool, ...@@ -189,69 +229,60 @@ func (a *action) checkValidSupervisionNode(config *pt.ParaNodeAddrConfig) (bool,
return false, nil return false, nil
} }
func (a *action) checkSupervisionNodeGroupExist(title string) error { func (a *action) checkSupervisionNodeGroupExist(title string) (error, bool) {
key := calcParaSupervisionNodeGroupAddrsKey(title) key := calcParaSupervisionNodeGroupAddrsKey(title)
_, err := a.db.Get(key) value, err := a.db.Get(key)
if err != nil && !isNotFound(err) { if err != nil && !isNotFound(err) {
return err return err, false
} }
//if value != nil { if value != nil {
// clog.Error("node group apply, group existed") return nil, true
// return pt.ErrParaSupervisionNodeGroupExisted }
//}
return nil return nil, false
} }
func (a *action) supervisionNodeGroupCreate(status *pt.ParaNodeGroupStatus) (*types.Receipt, error) { func (a *action) supervisionNodeGroupCreate(status *pt.ParaNodeIdStatus) (*types.Receipt, error) {
nodes := strings.Split(status.TargetAddrs, ",")
var item types.ConfigItem var item types.ConfigItem
key := calcParaSupervisionNodeGroupAddrsKey(status.Title) key := calcParaSupervisionNodeGroupAddrsKey(status.Title)
item.Key = string(key) item.Key = string(key)
emptyValue := &types.ArrayConfig{Value: make([]string, 0)} emptyValue := &types.ArrayConfig{Value: make([]string, 0)}
arr := types.ConfigItem_Arr{Arr: emptyValue} arr := types.ConfigItem_Arr{Arr: emptyValue}
item.Value = &arr item.Value = &arr
item.GetArr().Value = append(item.GetArr().Value, nodes...) item.GetArr().Value = append(item.GetArr().Value, status.TargetAddr)
item.Addr = a.fromaddr item.Addr = a.fromaddr
receipt := makeParaSupervisionNodeGroupReceipt(status.Title, nil, &item) receipt := makeParaSupervisionNodeGroupReceipt(status.Title, nil, &item)
var blsPubKeys []string status.Status = pt.ParacrossSupervisionNodeApprove
if len(status.BlsPubKeys) > 0 { r := makeSupervisionNodeConfigReceipt(a.fromaddr, nil, nil, status)
blsPubKeys = strings.Split(status.BlsPubKeys, ",") receipt = mergeReceipt(receipt, r)
}
//update addr status
for i, addr := range nodes {
stat := &pt.ParaNodeIdStatus{
Id: status.Id + "-" + strconv.Itoa(i),
Status: pt.ParacrossSupervisionNodeApprove,
Title: status.Title,
TargetAddr: addr,
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) r, err := a.updateSupervisionNodeAddrStatus(status)
if err != nil { if err != nil {
return nil, err return nil, err
}
receipt = mergeReceipt(receipt, r)
} }
receipt = mergeReceipt(receipt, r)
return receipt, nil return receipt, nil
} }
func getSupervisionNodeAddr(db dbm.KV, title, addr string) (*pt.ParaNodeAddrIdStatus, error) {
key := calcParaNodeAddrKey(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
}
//由于propasal id 和quit id分开,quit id不知道对应addr proposal id的coinfrozen信息,需要维护一个围绕addr的数据库结构信息 //由于propasal id 和quit id分开,quit id不知道对应addr proposal id的coinfrozen信息,需要维护一个围绕addr的数据库结构信息
func (a *action) updateSupervisionNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt, error) { func (a *action) updateSupervisionNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*types.Receipt, error) {
//cfg := a.api.GetConfig() //cfg := a.api.GetConfig()
addrStat, err := getSupervisionNodeAddr(a.db, stat.Title, stat.TargetAddr) addrStat, err := getNodeAddr(a.db, stat.Title, stat.TargetAddr)
if err != nil { if err != nil {
if !isNotFound(err) { if !isNotFound(err) {
return nil, errors.Wrapf(err, "nodeAddr:%s get error", stat.TargetAddr) return nil, errors.Wrapf(err, "nodeAddr:%s get error", stat.TargetAddr)
...@@ -267,18 +298,41 @@ func (a *action) updateSupervisionNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*ty ...@@ -267,18 +298,41 @@ func (a *action) updateSupervisionNodeAddrStatus(stat *pt.ParaNodeIdStatus) (*ty
} }
preStat := *addrStat preStat := *addrStat
stat.Status = pt.ParacrossSupervisionNodeApprove if stat.Status == pt.ParacrossSupervisionNodeQuit {
addrStat.ProposalId = stat.Id proposalStat, err := getNodeID(a.db, addrStat.ProposalId)
addrStat.QuitId = "" if err != nil {
return makeParaSupervisionNodeStatusReceipt(a.fromaddr, &preStat, addrStat), nil return nil, errors.Wrapf(err, "nodeAddr:%s quiting wrong proposeid:%s", stat.TargetAddr, addrStat.ProposalId)
}
addrStat.Status = pt.ParacrossSupervisionNodeQuit
addrStat.QuitId = stat.Id
receipt := makeParaSupervisionNodeStatusReceipt(a.fromaddr, &preStat, addrStat)
cfg := a.api.GetConfig()
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
} else {
stat.Status = pt.ParacrossSupervisionNodeApprove
addrStat.ProposalId = stat.Id
addrStat.QuitId = ""
return makeParaSupervisionNodeStatusReceipt(a.fromaddr, &preStat, addrStat), nil
}
} }
func (a *action) supervisionNodeGroupApply(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) { func (a *action) supervisionNodeApply(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
// 不能跟授权节点一致 // 必须要有授权节点 监督节点才有意义 判断是否存在授权节点
addrExist, err := a.checkValidNode(config) addrExist, err := a.checkValidNode(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 不能跟授权节点一致
if addrExist { if addrExist {
clog.Debug("supervisionNodeGroup Apply", "config.Addr", config.Addr, "err", "config.Addr existed in super group") clog.Debug("supervisionNodeGroup Apply", "config.Addr", config.Addr, "err", "config.Addr existed in super group")
return nil, pt.ErrParaNodeAddrExisted return nil, pt.ErrParaNodeAddrExisted
...@@ -317,108 +371,183 @@ func (a *action) supervisionNodeGroupApply(config *pt.ParaNodeAddrConfig) (*type ...@@ -317,108 +371,183 @@ func (a *action) supervisionNodeGroupApply(config *pt.ParaNodeAddrConfig) (*type
receipt.Logs = append(receipt.Logs, r.Logs...) receipt.Logs = append(receipt.Logs, r.Logs...)
} }
// 判断申请节点之前没有申请或者状态不是申请退出 stat := &pt.ParaNodeIdStatus{
//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.ParacrossSupervisionNodeQuit {
// return nil, errors.Wrapf(pt.ErrParaSupervisionNodeGroupExisted, "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)), Id: calcParaSupervisionNodeGroupIDKey(config.Title, common.ToHex(a.txhash)),
Status: pt.ParacrossSupervisionNodeApply, Status: pt.ParacrossSupervisionNodeApply,
Title: config.Title, Title: config.Title,
TargetAddrs: targetAddrs, TargetAddr: config.Addr,
BlsPubKeys: blsPubKeys, BlsPubKey: config.BlsPubKey,
CoinsFrozen: config.CoinsFrozen, CoinsFrozen: config.CoinsFrozen,
FromAddr: a.fromaddr, FromAddr: a.fromaddr,
Height: a.height, Height: a.height,
} }
r := makeSupervisionNodeGroupIDReceipt(a.fromaddr, nil, stat) r := makeSupervisionNodeConfigReceipt(a.fromaddr, config, nil, stat)
//r := makeSupervisionNodeIDReceipt(a.fromaddr, nil, stat)
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) supervisionNodeGroupApprove(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) { func (a *action) supervisionNodeApprove(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
//只在主链检查 //只在主链检查
if !cfg.IsPara() && !isSuperManager(cfg, a.fromaddr) { if !cfg.IsPara() && !isSuperManager(cfg, a.fromaddr) {
return nil, errors.Wrapf(types.ErrNotAllow, "node group approve not supervision manager:%s", a.fromaddr) return nil, errors.Wrapf(types.ErrNotAllow, "node group approve not supervision manager:%s", a.fromaddr)
} }
id, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id) apply, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if config.Title != id.Title { if config.Title != apply.Title {
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", config.Title, id.Title) return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", config.Title, apply.Title)
} }
return a.supervisionNodeGroupApproveApply(config, id) // 判断监督账户组是否已经存在
} err, exist := a.checkSupervisionNodeGroupExist(config.Title)
func (a *action) supervisionNodeGroupApproveApply(config *pt.ParaNodeAddrConfig, apply *pt.ParaNodeGroupStatus) (*types.Receipt, error) {
err := a.checkSupervisionNodeGroupExist(config.Title)
if err != nil { if err != nil {
return nil, err 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) if !exist {
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 supervision node group
r, err := a.supervisionNodeGroupCreate(apply)
if err != nil {
return nil, errors.Wrapf(err, "nodegroup create:title:%s,addrs:%s", config.Title, apply.TargetAddr)
}
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
stat := &pt.ParaNodeGroupStatus{
Id: apply.Id,
Status: apply.Status,
Title: apply.Title,
TargetAddrs: apply.TargetAddr,
BlsPubKeys: apply.BlsPubKey,
CoinsFrozen: apply.CoinsFrozen,
FromAddr: apply.FromAddr,
Height: apply.Height,
}
copyStat := *stat
stat.Status = pt.ParacrossSupervisionNodeApprove
apply.Status = pt.ParacrossSupervisionNodeApprove
apply.Height = a.height
r = makeSupervisionNodeIDReceipt(a.fromaddr, &copyStat, stat)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
r = makeParaSupervisionNodeGroupStatusReceipt(config.Title, a.fromaddr, nil, stat)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
//不允许主链成功平行链失败导致不一致的情况,这里如果失败则手工设置init stage 默认设置自共识
if cfg.IsPara() {
r = supervisionSelfConsentInitStage(cfg)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
}
return receipt, nil
} }
// 监督账户组已经存在
copyStat := proto.Clone(apply).(*pt.ParaNodeIdStatus)
receipt := &types.Receipt{Ty: types.ExecOk} receipt := &types.Receipt{Ty: types.ExecOk}
//create the node group
r, err := a.supervisionNodeGroupCreate(apply) r, err := updateSupervisionNodeGroup(a.db, config.Title, apply.TargetAddr, true)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "nodegroup create:title:%s,addrs:%s", config.Title, apply.TargetAddrs) return nil, err
} }
receipt.KV = append(receipt.KV, r.KV...) receipt = mergeReceipt(receipt, r)
receipt.Logs = append(receipt.Logs, r.Logs...)
r, err = a.updateSupervisionNodeAddrStatus(apply)
if err != nil {
return nil, err
}
receipt = mergeReceipt(receipt, r)
copyStat := *apply
apply.Status = pt.ParacrossSupervisionNodeApprove apply.Status = pt.ParacrossSupervisionNodeApprove
apply.Height = a.height apply.Height = a.height
r = makeSupervisionNodeGroupIDReceipt(a.fromaddr, &copyStat, apply) r = makeSupervisionNodeConfigReceipt(a.fromaddr, config, copyStat, apply)
receipt.KV = append(receipt.KV, r.KV...) receipt = mergeReceipt(receipt, r)
receipt.Logs = append(receipt.Logs, r.Logs...) return receipt, nil
}
func (a *action) supervisionNodeQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
addrExist, err := a.checkValidSupervisionNode(config)
if err != nil {
return nil, err
}
if !addrExist {
return nil, errors.Wrapf(pt.ErrParaSupervisionNodeAddrNotExisted, "nodeAddr not existed:%s", config.Addr)
}
status, err := getSupervisionNodeAddr(a.db, config.Title, config.Addr)
if err != nil {
return nil, errors.Wrapf(err, "nodeAddr:%s get error", config.Addr)
}
if status.Status != pt.ParacrossSupervisionNodeApprove {
return nil, errors.Wrapf(pt.ErrParaSupervisionNodeAddrNotExisted, "nodeAddr:%s status:%d", config.Addr, status.Status)
}
r = makeParaSupervisionNodeGroupStatusReceipt(config.Title, a.fromaddr, nil, apply)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
if cfg.IsPara() && cfg.IsDappFork(a.height, pt.ParaX, pt.ForkParaSelfConsStages) { stat := &pt.ParaNodeIdStatus{
//不允许主链成功平行链失败导致不一致的情况,这里如果失败则手工设置init stage ??? Id: calcParaSupervisionNodeGroupIDKey(config.Title, common.ToHex(a.txhash)),
r = supervisionSelfConsentInitStage(cfg) Status: pt.ParacrossSupervisionNodeQuit,
receipt.KV = append(receipt.KV, r.KV...) Title: config.Title,
receipt.Logs = append(receipt.Logs, r.Logs...) TargetAddr: config.Addr,
FromAddr: a.fromaddr,
Height: a.height,
} }
//只能提案发起人或超级节点可以撤销
if a.fromaddr != status.Addr && !cfg.IsPara() && !isSuperManager(cfg, a.fromaddr) {
return nil, errors.Wrapf(types.ErrNotAllow, "id create by:%s,not by:%s", status.Addr, a.fromaddr)
}
if config.Title != status.Title {
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", config.Title, status.Title)
}
receipt := &types.Receipt{Ty: types.ExecOk}
r, err := updateSupervisionNodeGroup(a.db, config.Title, stat.TargetAddr, false)
if err != nil {
return nil, err
}
receipt = mergeReceipt(receipt, r)
r, err = a.updateSupervisionNodeAddrStatus(stat)
if err != nil {
return nil, err
}
receipt = mergeReceipt(receipt, r)
r = makeSupervisionNodeConfigReceipt(a.fromaddr, config, nil, stat)
receipt = mergeReceipt(receipt, r)
return receipt, nil return receipt, nil
} }
func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) { func (a *action) supervisionNodeCancel(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
cfg := a.api.GetConfig() cfg := a.api.GetConfig()
status, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id) status, err := getSupervisionNodeGroupID(cfg, a.db, config.Title, a.exec.GetMainHeight(), config.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
//只能提案发起人撤销 //只能提案发起人可以撤销
if a.fromaddr != status.FromAddr { if a.fromaddr != status.FromAddr {
return nil, errors.Wrapf(types.ErrNotAllow, "id create by:%s,not by:%s", status.FromAddr, a.fromaddr) return nil, errors.Wrapf(types.ErrNotAllow, "id create by:%s,not by:%s", status.FromAddr, a.fromaddr)
} }
...@@ -427,17 +556,14 @@ func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types ...@@ -427,17 +556,14 @@ func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "config title:%s,id title:%s", 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.ParacrossSupervisionNodeApply { if status.Status != pt.ParacrossSupervisionNodeApply {
return nil, errors.Wrapf(pt.ErrParaNodeOpStatusWrong, "node group apply not apply:%d", status.Status) return nil, errors.Wrapf(pt.ErrParaNodeOpStatusWrong, "config id:%s,status:%d", config.Id, status.Status)
} }
applyAddrs := strings.Split(status.TargetAddrs, ",")
receipt := &types.Receipt{Ty: types.ExecOk} receipt := &types.Receipt{Ty: types.ExecOk}
//main chain //main chain
if !cfg.IsPara() { if !cfg.IsPara() {
r, err := a.nodeGroupCoinsActive(status.FromAddr, status.CoinsFrozen, int64(len(applyAddrs))) r, err := a.nodeGroupCoinsActive(status.FromAddr, status.CoinsFrozen, 1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -445,11 +571,11 @@ func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types ...@@ -445,11 +571,11 @@ func (a *action) supervisionNodeGroupQuit(config *pt.ParaNodeAddrConfig) (*types
receipt.Logs = append(receipt.Logs, r.Logs...) receipt.Logs = append(receipt.Logs, r.Logs...)
} }
copyStat := *status copyStat := proto.Clone(status).(*pt.ParaNodeIdStatus)
status.Status = pt.ParacrossSupervisionNodeQuit status.Status = pt.ParacrossSupervisionNodeCancel
status.Height = a.height status.Height = a.height
r := makeSupervisionNodeGroupIDReceipt(a.fromaddr, &copyStat, status) r := makeSupervisionNodeConfigReceipt(a.fromaddr, config, copyStat, status)
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...)
...@@ -461,19 +587,23 @@ func (a *action) SupervisionNodeGroupConfig(config *pt.ParaNodeAddrConfig) (*typ ...@@ -461,19 +587,23 @@ func (a *action) SupervisionNodeGroupConfig(config *pt.ParaNodeAddrConfig) (*typ
if !validTitle(cfg, config.Title) { if !validTitle(cfg, config.Title) {
return nil, pt.ErrInvalidTitle return nil, pt.ErrInvalidTitle
} }
if !types.IsParaExecName(string(a.tx.Execer)) && cfg.IsDappFork(a.exec.GetMainHeight(), pt.ParaX, pt.ForkParaSupervision) { if !types.IsParaExecName(string(a.tx.Execer)) {
return nil, errors.Wrapf(types.ErrInvalidParam, "exec=%s,should prefix with user.p.", string(a.tx.Execer)) return nil, errors.Wrapf(types.ErrInvalidParam, "exec=%s,should prefix with user.p.", string(a.tx.Execer))
} }
if (config.Op == pt.ParacrossSupervisionNodeApprove || config.Op == pt.ParacrossSupervisionNodeQuit) && config.Id == "" { if (config.Op == pt.ParacrossSupervisionNodeApprove || config.Op == pt.ParacrossSupervisionNodeCancel) && config.Id == "" {
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
} }
if config.Op == pt.ParacrossSupervisionNodeApply { if config.Op == pt.ParacrossSupervisionNodeApply {
return a.supervisionNodeGroupApply(config) return a.supervisionNodeApply(config)
} else if config.Op == pt.ParacrossSupervisionNodeApprove { } else if config.Op == pt.ParacrossSupervisionNodeApprove {
return a.supervisionNodeGroupApprove(config) return a.supervisionNodeApprove(config)
} else if config.Op == pt.ParacrossSupervisionNodeQuit { } else if config.Op == pt.ParacrossSupervisionNodeQuit {
return a.supervisionNodeGroupQuit(config) // 退出 group
return a.supervisionNodeQuit(config)
} else if config.Op == pt.ParacrossSupervisionNodeCancel {
// 撤销未批准的申请
return a.supervisionNodeCancel(config)
} }
return nil, pt.ErrParaUnSupportNodeOper return nil, pt.ErrParaUnSupportNodeOper
......
...@@ -20,15 +20,16 @@ func createRawSupervisionNodeConfigTx(config *pt.ParaNodeAddrConfig) *types.Tran ...@@ -20,15 +20,16 @@ func createRawSupervisionNodeConfigTx(config *pt.ParaNodeAddrConfig) *types.Tran
} }
func (suite *NodeManageTestSuite) testSupervisionExec() { func (suite *NodeManageTestSuite) testSupervisionExec() {
suite.testSupervisionNodeConfigQuit() suite.testSupervisionNodeConfigCancel()
suite.testSupervisionNodeConfigApprove(Account14K, PrivKey14K) suite.testSupervisionNodeConfigApprove(Account14K, PrivKey14K)
suite.testSupervisionNodeConfigApprove(Account1Ku, PrivKey1Ku) suite.testSupervisionNodeConfigApprove(Account1Ku, PrivKey1Ku)
suite.testSupervisionNodeConfigApprove(Account1M3, PrivKey1M3) suite.testSupervisionNodeConfigApprove(Account1M3, PrivKey1M3)
suite.testSupervisionNodeError() suite.testSupervisionNodeError()
suite.testSupervisionQuery() suite.testSupervisionQuery()
suite.testSupervisionNodeQuit()
} }
func (suite *NodeManageTestSuite) testSupervisionNodeConfigQuit() { func (suite *NodeManageTestSuite) testSupervisionNodeConfigCancel() {
// Apply // Apply
config := &pt.ParaNodeAddrConfig{ config := &pt.ParaNodeAddrConfig{
Title: chain33TestCfg.GetTitle(), Title: chain33TestCfg.GetTitle(),
...@@ -46,7 +47,7 @@ func (suite *NodeManageTestSuite) testSupervisionNodeConfigQuit() { ...@@ -46,7 +47,7 @@ func (suite *NodeManageTestSuite) testSupervisionNodeConfigQuit() {
// Quit // Quit
config = &pt.ParaNodeAddrConfig{ config = &pt.ParaNodeAddrConfig{
Title: chain33TestCfg.GetTitle(), Title: chain33TestCfg.GetTitle(),
Op: pt.ParacrossSupervisionNodeQuit, Op: pt.ParacrossSupervisionNodeCancel,
Id: g.Current.Id, Id: g.Current.Id,
} }
tx = createRawSupervisionNodeConfigTx(config) tx = createRawSupervisionNodeConfigTx(config)
...@@ -100,11 +101,32 @@ func (suite *NodeManageTestSuite) testSupervisionNodeError() { ...@@ -100,11 +101,32 @@ func (suite *NodeManageTestSuite) testSupervisionNodeError() {
assert.Equal(suite.T(), err, pt.ErrParaNodeAddrExisted) assert.Equal(suite.T(), err, pt.ErrParaNodeAddrExisted)
} }
func (suite *NodeManageTestSuite) testSupervisionNodeQuit() {
config := &pt.ParaNodeAddrConfig{
Title: chain33TestCfg.GetTitle(),
Op: pt.ParacrossSupervisionNodeQuit,
Addr: Account1Ku,
}
tx := createRawSupervisionNodeConfigTx(config)
receipt := nodeCommit(suite, PrivKey14K, tx)
assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
assert.Len(suite.T(), receipt.KV, 3)
assert.Len(suite.T(), receipt.Logs, 3)
assert.Equal(suite.T(), int32(pt.TyLogParaSupervisionNodeGroupAddrsUpdate), receipt.Logs[0].Ty)
ret, err := suite.exec.Query_GetSupervisionNodeGroupAddrs(&pt.ReqParacrossNodeInfo{Title: chain33TestCfg.GetTitle()})
suite.Nil(err)
resp, ok := ret.(*types.ReplyConfig)
assert.Equal(suite.T(), ok, true)
assert.Equal(suite.T(), resp.Value, "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt,1M3XCbWVxAPBH5AR8VmLky4ZtDdGgC6ugD")
}
func checkSupervisionGroupApplyReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) { func checkSupervisionGroupApplyReceipt(suite *NodeManageTestSuite, receipt *types.Receipt) {
assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk)) assert.Equal(suite.T(), receipt.Ty, int32(types.ExecOk))
assert.Len(suite.T(), receipt.KV, 1) assert.Len(suite.T(), receipt.KV, 1)
assert.Len(suite.T(), receipt.Logs, 1) assert.Len(suite.T(), receipt.Logs, 1)
assert.Equal(suite.T(), int32(pt.TyLogParaSupervisionNodeGroupConfig), receipt.Logs[0].Ty) assert.Equal(suite.T(), int32(pt.TyLogParaSupervisionNodeConfig), receipt.Logs[0].Ty)
} }
func (suite *NodeManageTestSuite) testSupervisionQuery() { func (suite *NodeManageTestSuite) testSupervisionQuery() {
...@@ -114,7 +136,7 @@ func (suite *NodeManageTestSuite) testSupervisionQuery() { ...@@ -114,7 +136,7 @@ func (suite *NodeManageTestSuite) testSupervisionQuery() {
assert.Equal(suite.T(), ok, true) assert.Equal(suite.T(), ok, true)
assert.Equal(suite.T(), resp.Value, "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt,1KufZaLTKVAy37AsXNd9bsva5WZvP8w5uG,1M3XCbWVxAPBH5AR8VmLky4ZtDdGgC6ugD") assert.Equal(suite.T(), resp.Value, "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt,1KufZaLTKVAy37AsXNd9bsva5WZvP8w5uG,1M3XCbWVxAPBH5AR8VmLky4ZtDdGgC6ugD")
ret, err = suite.exec.Query_GetSupervisionNodeAddrInfo(&pt.ReqParacrossNodeInfo{Title: chain33TestCfg.GetTitle(), Addr: Account14K}) ret, err = suite.exec.Query_GetNodeAddrInfo(&pt.ReqParacrossNodeInfo{Title: chain33TestCfg.GetTitle(), Addr: Account14K})
suite.Nil(err) suite.Nil(err)
resp2, ok := ret.(*pt.ParaNodeAddrIdStatus) resp2, ok := ret.(*pt.ParaNodeAddrIdStatus)
assert.Equal(suite.T(), ok, true) assert.Equal(suite.T(), ok, true)
......
...@@ -31,13 +31,15 @@ message ParacrossHeightStatus { ...@@ -31,13 +31,15 @@ message ParacrossHeightStatus {
} }
message ParacrossHeightStatusRsp { message ParacrossHeightStatusRsp {
int32 status = 1; int32 status = 1;
string title = 2; string title = 2;
int64 height = 3; int64 height = 3;
int64 mainHeight = 4; int64 mainHeight = 4;
string mainHash = 5; string mainHash = 5;
repeated string commitAddrs = 6; repeated string commitAddrs = 6;
repeated string commitBlockHash = 7; repeated string commitBlockHash = 7;
repeated string commitSupervisionAddrs = 8;
repeated string commitSupervisionBlockHash = 9;
} }
message ParacrossStatus { message ParacrossStatus {
......
...@@ -65,4 +65,6 @@ var ( ...@@ -65,4 +65,6 @@ var (
ErrParaSupervisionNodeGroupNotSet = errors.New("ErrParaSupervisionNodeGroupNotSet") ErrParaSupervisionNodeGroupNotSet = errors.New("ErrParaSupervisionNodeGroupNotSet")
//ErrParaSupervisionNodeGroupExisted para config group taked over alreay //ErrParaSupervisionNodeGroupExisted para config group taked over alreay
ErrParaSupervisionNodeGroupExisted = errors.New("ErrParaSupervisionNodesExisted") ErrParaSupervisionNodeGroupExisted = errors.New("ErrParaSupervisionNodesExisted")
//ErrParaSupervisionNodeAddrNotExisted node addr not exist in supervision group
ErrParaSupervisionNodeAddrNotExisted = errors.New("ErrParaSupervisionNodeAddrNotExisted")
) )
...@@ -177,6 +177,7 @@ const ( ...@@ -177,6 +177,7 @@ const (
ParacrossSupervisionNodeApply = iota + 1 ParacrossSupervisionNodeApply = iota + 1
ParacrossSupervisionNodeApprove ParacrossSupervisionNodeApprove
ParacrossSupervisionNodeQuit ParacrossSupervisionNodeQuit
ParacrossSupervisionNodeCancel
) )
// 0 普通节点共识 1 授权节点正在共识 2 监督节点正在共识 // 0 普通节点共识 1 授权节点正在共识 2 监督节点正在共识
...@@ -334,6 +335,10 @@ func GetDappForkHeight(cfg *types.Chain33Config, forkKey string) int64 { ...@@ -334,6 +335,10 @@ func GetDappForkHeight(cfg *types.Chain33Config, forkKey string) int64 {
if forkHeight <= 0 { if forkHeight <= 0 {
forkHeight = types.MaxHeight forkHeight = types.MaxHeight
} }
if key == ForkParaSupervision { // ????
forkHeight = types.Conf(cfg, ParaPrefixConsSubConf).GInt(key)
}
} else { } else {
forkHeight = cfg.GetDappFork(ParaX, forkKey) forkHeight = cfg.GetDappFork(ParaX, forkKey)
......
...@@ -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.
...@@ -219,16 +218,18 @@ func (m *ParacrossHeightStatus) GetSupervisionDetails() *ParacrossStatusDetails ...@@ -219,16 +218,18 @@ func (m *ParacrossHeightStatus) GetSupervisionDetails() *ParacrossStatusDetails
} }
type ParacrossHeightStatusRsp struct { type ParacrossHeightStatusRsp struct {
Status int32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` Status int32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"`
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"`
MainHeight int64 `protobuf:"varint,4,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"` MainHeight int64 `protobuf:"varint,4,opt,name=mainHeight,proto3" json:"mainHeight,omitempty"`
MainHash string `protobuf:"bytes,5,opt,name=mainHash,proto3" json:"mainHash,omitempty"` MainHash string `protobuf:"bytes,5,opt,name=mainHash,proto3" json:"mainHash,omitempty"`
CommitAddrs []string `protobuf:"bytes,6,rep,name=commitAddrs,proto3" json:"commitAddrs,omitempty"` CommitAddrs []string `protobuf:"bytes,6,rep,name=commitAddrs,proto3" json:"commitAddrs,omitempty"`
CommitBlockHash []string `protobuf:"bytes,7,rep,name=commitBlockHash,proto3" json:"commitBlockHash,omitempty"` CommitBlockHash []string `protobuf:"bytes,7,rep,name=commitBlockHash,proto3" json:"commitBlockHash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` CommitSupervisionAddrs []string `protobuf:"bytes,8,rep,name=commitSupervisionAddrs,proto3" json:"commitSupervisionAddrs,omitempty"`
XXX_unrecognized []byte `json:"-"` CommitSupervisionBlockHash []string `protobuf:"bytes,9,rep,name=commitSupervisionBlockHash,proto3" json:"commitSupervisionBlockHash,omitempty"`
XXX_sizecache int32 `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *ParacrossHeightStatusRsp) Reset() { *m = ParacrossHeightStatusRsp{} } func (m *ParacrossHeightStatusRsp) Reset() { *m = ParacrossHeightStatusRsp{} }
...@@ -305,6 +306,20 @@ func (m *ParacrossHeightStatusRsp) GetCommitBlockHash() []string { ...@@ -305,6 +306,20 @@ func (m *ParacrossHeightStatusRsp) GetCommitBlockHash() []string {
return nil return nil
} }
func (m *ParacrossHeightStatusRsp) GetCommitSupervisionAddrs() []string {
if m != nil {
return m.CommitSupervisionAddrs
}
return nil
}
func (m *ParacrossHeightStatusRsp) GetCommitSupervisionBlockHash() []string {
if m != nil {
return m.CommitSupervisionBlockHash
}
return nil
}
type ParacrossStatus struct { type ParacrossStatus struct {
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
...@@ -4490,199 +4505,198 @@ func init() { ...@@ -4490,199 +4505,198 @@ 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{
// 2980 bytes of a gzipped FileDescriptorProto // 3008 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0x4d, 0x6c, 0x24, 0x47, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x1a, 0x4b, 0x6c, 0x24, 0x47,
0xd5, 0xee, 0xf9, 0xf3, 0xcc, 0xb3, 0xc7, 0x5e, 0x77, 0xbc, 0xce, 0xc4, 0x49, 0x56, 0x56, 0x2b, 0xd5, 0x3d, 0x3f, 0xcf, 0x3c, 0x7b, 0xec, 0xdd, 0xce, 0xae, 0x33, 0x71, 0x92, 0x95, 0xd5, 0x0a,
0x5f, 0xe4, 0x8f, 0x6c, 0xbc, 0x59, 0x27, 0x04, 0x45, 0x08, 0x41, 0xec, 0xdd, 0x64, 0xac, 0xac, 0x91, 0x21, 0x9b, 0xdd, 0xac, 0x13, 0x16, 0x45, 0x88, 0x4f, 0xec, 0xdd, 0xc4, 0x56, 0xd6, 0x61,
0xc3, 0xa6, 0xec, 0x00, 0x52, 0x04, 0xa2, 0x3d, 0x53, 0xb6, 0x5b, 0x99, 0xe9, 0x9e, 0xed, 0xea, 0x53, 0x76, 0x00, 0x29, 0x02, 0xd1, 0x9e, 0x29, 0xdb, 0xad, 0xcc, 0x74, 0xcf, 0x76, 0xf5, 0x64,
0xc9, 0xda, 0x08, 0x29, 0x1c, 0x80, 0x1b, 0x12, 0x17, 0x24, 0xb8, 0x70, 0x81, 0x1b, 0x12, 0x27, 0x6d, 0x84, 0x14, 0x0e, 0xc0, 0x0d, 0x89, 0x0b, 0x12, 0x5c, 0xb8, 0xc0, 0x0d, 0x89, 0x13, 0x67,
0xce, 0x1c, 0x90, 0xb8, 0x44, 0x5c, 0xc2, 0x91, 0x1b, 0x37, 0x10, 0x47, 0x6e, 0x9c, 0xd0, 0x7b, 0x0e, 0x48, 0x5c, 0x22, 0x2e, 0xe1, 0xc8, 0x09, 0x8e, 0x88, 0x23, 0x37, 0x4e, 0xe8, 0xbd, 0xaa,
0x55, 0xd5, 0x5d, 0x55, 0xdd, 0x33, 0x76, 0xb2, 0xb9, 0x70, 0xeb, 0xf7, 0xfa, 0x55, 0xd5, 0xfb, 0xea, 0xae, 0xaa, 0xee, 0x69, 0x3b, 0xd9, 0x5c, 0xb8, 0xcd, 0x7b, 0xf5, 0xaa, 0xea, 0xfd, 0xeb,
0xaf, 0xf7, 0x5e, 0x37, 0xac, 0x4e, 0xc2, 0x34, 0x1c, 0xa4, 0x89, 0x10, 0x3b, 0x93, 0x34, 0xc9, 0xbd, 0x37, 0x0d, 0xab, 0xd3, 0x30, 0x0d, 0x87, 0x69, 0x22, 0xc4, 0xad, 0x69, 0x9a, 0x64, 0x89,
0x12, 0xbf, 0x99, 0x5d, 0x4e, 0xb8, 0xd8, 0x5c, 0xcb, 0xd2, 0x30, 0x16, 0xe1, 0x20, 0x8b, 0x92, 0xdf, 0xce, 0xce, 0xa7, 0x5c, 0xac, 0x5f, 0xcd, 0xd2, 0x30, 0x16, 0xe1, 0x30, 0x8b, 0x92, 0x58,
0x58, 0xbe, 0xd9, 0x5c, 0x1e, 0x24, 0xe3, 0x71, 0x0e, 0xdd, 0x38, 0x19, 0x25, 0x83, 0x0f, 0x07, 0xae, 0xac, 0x2f, 0x0f, 0x93, 0xc9, 0x24, 0x87, 0xae, 0x1c, 0x8d, 0x93, 0xe1, 0x07, 0xc3, 0xd3,
0xe7, 0x61, 0xa4, 0x30, 0xc1, 0x03, 0xd8, 0x78, 0xa8, 0x37, 0x3b, 0xca, 0xc2, 0x6c, 0x2a, 0xee, 0x30, 0x52, 0x98, 0xe0, 0x01, 0xac, 0x3d, 0xd4, 0x87, 0x1d, 0x64, 0x61, 0x36, 0x13, 0xf7, 0x78,
0xf1, 0x2c, 0x8c, 0x46, 0xc2, 0x5f, 0x87, 0x66, 0x38, 0x1c, 0xa6, 0xa2, 0xe7, 0x6d, 0xd5, 0xb7, 0x16, 0x46, 0x63, 0xe1, 0x5f, 0x83, 0x76, 0x38, 0x1a, 0xa5, 0x62, 0xe0, 0x6d, 0x34, 0x37, 0x7b,
0x3b, 0x4c, 0x02, 0xfe, 0x73, 0xd0, 0xa1, 0x3d, 0xfa, 0xa1, 0x38, 0xef, 0xd5, 0xb6, 0xea, 0xdb, 0x4c, 0x02, 0xfe, 0x73, 0xd0, 0xa3, 0x33, 0x76, 0x43, 0x71, 0x3a, 0x68, 0x6c, 0x34, 0x37, 0x97,
0xcb, 0xac, 0x40, 0x04, 0x1f, 0xc0, 0xb3, 0xce, 0x6e, 0x7b, 0xf8, 0x4e, 0x6f, 0x79, 0x0b, 0x20, 0x59, 0x81, 0x08, 0xde, 0x87, 0x67, 0x9d, 0xd3, 0xb6, 0x71, 0x4d, 0x1f, 0x79, 0x03, 0x20, 0xa7,
0xa7, 0x95, 0xfb, 0x2e, 0x33, 0x03, 0x83, 0x9b, 0x67, 0x17, 0x8c, 0x8b, 0xe9, 0x28, 0x13, 0x7a, 0x95, 0xe7, 0x2e, 0x33, 0x03, 0x83, 0x87, 0x67, 0x67, 0x8c, 0x8b, 0xd9, 0x38, 0x13, 0xfa, 0xf0,
0xf3, 0x1c, 0x11, 0xfc, 0xb3, 0x06, 0x37, 0xf3, 0xdd, 0xfb, 0x3c, 0x3a, 0x3b, 0xcf, 0xe4, 0x19, 0x1c, 0x11, 0xfc, 0xab, 0x01, 0xd7, 0xf3, 0xd3, 0x77, 0x79, 0x74, 0x72, 0x9a, 0xc9, 0x3b, 0xfc,
0xfe, 0x06, 0xb4, 0x04, 0x3d, 0xf5, 0xbc, 0x2d, 0x6f, 0xbb, 0xc9, 0x14, 0x84, 0x22, 0x64, 0x51, 0x35, 0xe8, 0x08, 0xfa, 0x35, 0xf0, 0x36, 0xbc, 0xcd, 0x36, 0x53, 0x10, 0x8a, 0x90, 0x45, 0xd9,
0x36, 0xe2, 0xbd, 0xda, 0x96, 0x87, 0x22, 0x10, 0x80, 0xd4, 0xe7, 0xb4, 0xba, 0x57, 0xdf, 0xf2, 0x98, 0x0f, 0x1a, 0x1b, 0x1e, 0x8a, 0x40, 0x00, 0x52, 0x9f, 0xd2, 0xee, 0x41, 0x73, 0xc3, 0xdb,
0xb6, 0xeb, 0x4c, 0x41, 0xfe, 0x57, 0x60, 0x71, 0x28, 0x19, 0xed, 0x35, 0xb6, 0xbc, 0xed, 0xa5, 0x6c, 0x32, 0x05, 0xf9, 0x5f, 0x81, 0xc5, 0x91, 0x64, 0x74, 0xd0, 0xda, 0xf0, 0x36, 0x97, 0xb6,
0xdd, 0xe7, 0x77, 0x48, 0xad, 0x3b, 0xd5, 0x0a, 0x62, 0x9a, 0x1a, 0xc5, 0x1a, 0x87, 0x51, 0x2c, 0x9e, 0xbf, 0x45, 0x6a, 0xbd, 0x55, 0xad, 0x20, 0xa6, 0xa9, 0x51, 0xac, 0x49, 0x18, 0xc5, 0x92,
0x59, 0xea, 0x35, 0x69, 0x53, 0x03, 0xe3, 0x6f, 0x42, 0x9b, 0x20, 0x54, 0x59, 0x6b, 0xcb, 0xdb, 0xa5, 0x41, 0x9b, 0x0e, 0x35, 0x30, 0xfe, 0x3a, 0x74, 0x09, 0x42, 0x95, 0x75, 0x36, 0xbc, 0xcd,
0x5e, 0x66, 0x39, 0xec, 0xbf, 0x05, 0xcb, 0x27, 0x86, 0x8a, 0x7a, 0x8b, 0x74, 0x72, 0x50, 0x7d, 0x65, 0x96, 0xc3, 0xfe, 0x9b, 0xb0, 0x7c, 0x64, 0xa8, 0x68, 0xb0, 0x48, 0x37, 0x07, 0xd5, 0x37,
0xb2, 0xa9, 0x4c, 0x66, 0xad, 0xf3, 0x0f, 0xc1, 0x17, 0xd3, 0x09, 0x4f, 0x3f, 0x8a, 0x44, 0x94, 0x9b, 0xca, 0x64, 0xd6, 0x3e, 0x7f, 0x1f, 0x7c, 0x31, 0x9b, 0xf2, 0xf4, 0xc3, 0x48, 0x44, 0x49,
0xc4, 0x7a, 0xb7, 0xf6, 0x75, 0xe4, 0xa8, 0x58, 0x18, 0xfc, 0xc3, 0x83, 0x5e, 0xa5, 0xae, 0x99, 0xac, 0x4f, 0xeb, 0x5e, 0x46, 0x8e, 0x8a, 0x8d, 0xc1, 0x3f, 0x1a, 0x30, 0xa8, 0xd4, 0x35, 0x13,
0x98, 0x7c, 0x41, 0xea, 0xb6, 0xb5, 0xd6, 0x98, 0xab, 0xb5, 0x26, 0x6d, 0x58, 0x68, 0x6d, 0x0b, 0xd3, 0xcf, 0x49, 0xdd, 0xb6, 0xd6, 0x5a, 0xb5, 0x5a, 0x6b, 0xd3, 0x81, 0x85, 0xd6, 0x36, 0x60,
0x96, 0xd0, 0xaf, 0xa3, 0xec, 0x4d, 0xf2, 0xd0, 0x16, 0x79, 0xa8, 0x89, 0xf2, 0xb7, 0x61, 0x55, 0x09, 0xfd, 0x3a, 0xca, 0xde, 0x20, 0x0f, 0xed, 0x90, 0x87, 0x9a, 0x28, 0x7f, 0x13, 0x56, 0x25,
0x82, 0x7b, 0xb9, 0xb7, 0x2e, 0x12, 0x95, 0x8b, 0x0e, 0x7e, 0xe5, 0xc1, 0xaa, 0xa3, 0x99, 0x42, 0xb8, 0x9d, 0x7b, 0xeb, 0x22, 0x51, 0xb9, 0x68, 0xff, 0x2e, 0xac, 0x49, 0xd4, 0x41, 0xa1, 0x06,
0x12, 0xaf, 0x5a, 0x92, 0x9a, 0x25, 0x89, 0x15, 0x13, 0x75, 0x32, 0x70, 0x81, 0xf8, 0xcc, 0x72, 0x79, 0x6c, 0x97, 0x36, 0xcc, 0x59, 0xf5, 0xbf, 0x0e, 0xeb, 0xa5, 0x95, 0xe2, 0xb2, 0x1e, 0xed,
0x1a, 0xde, 0x11, 0xfc, 0xd6, 0x34, 0xc3, 0x7e, 0x12, 0x0b, 0x1e, 0x8b, 0xe9, 0x7c, 0x26, 0x51, 0xad, 0xa1, 0x08, 0x7e, 0xed, 0xc1, 0xaa, 0x63, 0x91, 0x42, 0x83, 0x5e, 0xb5, 0x06, 0x1b, 0x96,
0x35, 0xe7, 0xc5, 0x79, 0x92, 0x53, 0x13, 0xe5, 0xbf, 0x00, 0xdd, 0x81, 0xdc, 0xaa, 0x6f, 0xda, 0x06, 0xad, 0x58, 0x6c, 0x92, 0x63, 0x15, 0x88, 0x4f, 0xad, 0x5f, 0xc3, 0x2b, 0x83, 0xdf, 0x79,
0xc5, 0x46, 0xfa, 0x5f, 0x82, 0x1b, 0x0a, 0x51, 0x68, 0xb0, 0x41, 0x07, 0x95, 0xf0, 0xc1, 0x1f, 0x86, 0xf9, 0x77, 0x92, 0x58, 0xf0, 0x58, 0xcc, 0xea, 0x99, 0x44, 0x93, 0x9c, 0x16, 0xf7, 0x49,
0x3c, 0xf0, 0x91, 0xcd, 0x77, 0x93, 0x21, 0x47, 0xf5, 0xef, 0x27, 0xf1, 0x69, 0x74, 0x36, 0x83, 0x4e, 0x4d, 0x94, 0xff, 0x02, 0xf4, 0x87, 0xf2, 0xa8, 0x5d, 0xd3, 0x1f, 0x6c, 0xa4, 0xff, 0x25,
0xc1, 0x15, 0xa8, 0x25, 0x13, 0xe2, 0xab, 0xcb, 0x6a, 0xc9, 0x04, 0xe1, 0x68, 0x48, 0x3c, 0x74, 0xb8, 0xa2, 0x10, 0x85, 0x32, 0x5b, 0x74, 0x51, 0x09, 0x1f, 0xfc, 0xd1, 0x03, 0x1f, 0xd9, 0x7c,
0x58, 0x2d, 0x1a, 0xfa, 0x3e, 0x34, 0x30, 0xd5, 0xa8, 0xc3, 0xe8, 0x19, 0x77, 0xfa, 0x28, 0x1c, 0x27, 0x19, 0x71, 0x34, 0xca, 0x4e, 0x12, 0x1f, 0x47, 0x27, 0x73, 0x18, 0x5c, 0x81, 0x46, 0x32,
0x4d, 0x39, 0x29, 0xa8, 0xcb, 0x24, 0x20, 0xbd, 0x20, 0x8a, 0xc5, 0x5b, 0x69, 0xf2, 0x03, 0x1e, 0x25, 0xbe, 0xfa, 0xac, 0x91, 0x4c, 0x11, 0x8e, 0x46, 0xc4, 0x43, 0x8f, 0x35, 0xa2, 0x91, 0xef,
0x53, 0x68, 0xa1, 0xa8, 0x05, 0x4a, 0x5a, 0x46, 0x3c, 0x9c, 0x9e, 0xbc, 0xc3, 0x2f, 0x29, 0xb4, 0x43, 0x0b, 0x53, 0x9c, 0xba, 0x8c, 0x7e, 0xe3, 0x49, 0x1f, 0x86, 0xe3, 0x19, 0x27, 0x05, 0xf5,
0x3a, 0xac, 0x40, 0x04, 0xdf, 0x28, 0xb8, 0xfe, 0x56, 0x92, 0x71, 0xe9, 0xfb, 0x33, 0xf2, 0x1e, 0x99, 0x04, 0xa4, 0xf7, 0x45, 0xb1, 0x78, 0x33, 0x4d, 0x7e, 0xc8, 0x63, 0x0a, 0x69, 0x14, 0xb5,
0x72, 0x90, 0x64, 0x5c, 0xa6, 0xa5, 0x0e, 0x93, 0x40, 0xf0, 0x7b, 0x0f, 0xd6, 0x4d, 0xc1, 0x0f, 0x40, 0x49, 0xcb, 0x88, 0x87, 0xb3, 0xa3, 0xb7, 0xf9, 0x39, 0x85, 0x74, 0x8f, 0x15, 0x88, 0xe0,
0x86, 0xca, 0x36, 0x5a, 0x08, 0xcf, 0x10, 0xe2, 0x16, 0xc0, 0x24, 0x4d, 0x26, 0x89, 0x08, 0x47, 0x9b, 0x05, 0xd7, 0xdf, 0x4e, 0x32, 0x2e, 0x63, 0x6e, 0x4e, 0xbe, 0x45, 0x0e, 0x92, 0x8c, 0xcb,
0x07, 0x43, 0x15, 0x23, 0x06, 0x06, 0xdd, 0xeb, 0xd1, 0x34, 0xca, 0x0e, 0xb4, 0x32, 0x14, 0x64, 0x74, 0xd8, 0x63, 0x12, 0x08, 0xfe, 0xe0, 0xc1, 0x35, 0x53, 0xf0, 0xbd, 0x91, 0xb2, 0x8d, 0x16,
0x84, 0x5b, 0xa3, 0x3a, 0xdc, 0x9a, 0xa6, 0x7a, 0x2d, 0x91, 0x5b, 0xae, 0xc8, 0xbf, 0xa8, 0xc1, 0xc2, 0x33, 0x84, 0xb8, 0x01, 0x30, 0x4d, 0x93, 0x69, 0x22, 0xc2, 0xf1, 0xde, 0x48, 0xc5, 0xa6,
0x0d, 0xcd, 0x70, 0xce, 0xac, 0xb4, 0x80, 0x97, 0x5b, 0xa0, 0x38, 0xb0, 0x56, 0x7d, 0x60, 0xdd, 0x81, 0x41, 0xf7, 0x7a, 0x34, 0x8b, 0xb2, 0x3d, 0xad, 0x0c, 0x05, 0x19, 0x61, 0xde, 0xaa, 0x0e,
0x3c, 0xf0, 0x16, 0x40, 0x16, 0xa6, 0x67, 0x9c, 0x02, 0x4f, 0x59, 0xcd, 0xc0, 0xb8, 0x56, 0x6a, 0xf3, 0xb6, 0xa9, 0x5e, 0x4b, 0xe4, 0x8e, 0x2b, 0xf2, 0x2f, 0x1b, 0x70, 0x45, 0x33, 0x9c, 0x33,
0x96, 0xad, 0x74, 0x47, 0xeb, 0xb6, 0x45, 0xe9, 0xea, 0x19, 0x23, 0x5d, 0xd9, 0xb6, 0x51, 0x6a, 0x2b, 0x2d, 0xe0, 0xe5, 0x16, 0x28, 0x2e, 0x6c, 0x54, 0x5f, 0xd8, 0x34, 0x2f, 0xbc, 0x01, 0x90,
0xc7, 0x90, 0x39, 0x4d, 0x93, 0x31, 0x1d, 0x28, 0xad, 0x9a, 0xc3, 0x46, 0x90, 0xb6, 0xcb, 0x41, 0x85, 0xe9, 0x09, 0xa7, 0x80, 0x57, 0x56, 0x33, 0x30, 0xae, 0x95, 0xda, 0x65, 0x2b, 0xdd, 0xd6,
0xaa, 0xf5, 0xd2, 0x71, 0xf5, 0xf2, 0x47, 0x0f, 0x6e, 0x32, 0x3e, 0xe0, 0xd1, 0x24, 0xd3, 0xc7, 0xba, 0xed, 0x50, 0x9a, 0x7c, 0xc6, 0x48, 0x93, 0xb6, 0x6d, 0x94, 0xda, 0x31, 0x64, 0x8e, 0xd3,
0x2a, 0x27, 0xae, 0xb2, 0xe4, 0x5d, 0x68, 0x0d, 0xe8, 0x2d, 0x29, 0xa8, 0xcc, 0x71, 0x11, 0x03, 0x64, 0x42, 0x17, 0x4a, 0xab, 0xe6, 0xb0, 0x11, 0xa4, 0xdd, 0x72, 0x90, 0x6a, 0xbd, 0xf4, 0x5c,
0x4c, 0x11, 0xfa, 0x2f, 0x41, 0x63, 0x92, 0xf2, 0x8f, 0x48, 0x75, 0x4b, 0xbb, 0x4f, 0x3b, 0x0b, 0xbd, 0xfc, 0xc9, 0x83, 0xeb, 0x8c, 0x0f, 0x79, 0x34, 0xcd, 0xf4, 0xb5, 0xca, 0x89, 0xab, 0x2c,
0xb4, 0x29, 0x18, 0x11, 0xf9, 0x77, 0x61, 0x71, 0x30, 0x4d, 0x53, 0x1e, 0x67, 0xea, 0x26, 0x9a, 0x79, 0x07, 0x3a, 0x43, 0x5a, 0x25, 0x05, 0x95, 0x39, 0x2e, 0x62, 0x80, 0x29, 0x42, 0xff, 0x25,
0x49, 0xaf, 0xe9, 0x82, 0xdf, 0x78, 0xf0, 0xbc, 0x23, 0x00, 0x72, 0x81, 0x64, 0xef, 0x4f, 0x86, 0x68, 0x4d, 0x53, 0xfe, 0x21, 0xa9, 0x6e, 0x69, 0xeb, 0x69, 0x67, 0x83, 0x36, 0x05, 0x23, 0x22,
0x61, 0xc6, 0x2d, 0xa5, 0x79, 0x8e, 0xd2, 0xee, 0x28, 0xee, 0xa4, 0x38, 0xcf, 0x56, 0x88, 0xe3, 0xff, 0x0e, 0x2c, 0x0e, 0x67, 0x69, 0xca, 0xe3, 0x4c, 0xbd, 0x80, 0x73, 0xe9, 0x35, 0x5d, 0xf0,
0x70, 0xf8, 0xe5, 0x82, 0xc3, 0xfa, 0xd5, 0x6b, 0x72, 0x2e, 0xff, 0xed, 0xc1, 0xd3, 0x0e, 0x97, 0x5b, 0x0f, 0x9e, 0x77, 0x04, 0x40, 0x2e, 0x90, 0xec, 0xbd, 0xe9, 0x28, 0xcc, 0xb8, 0xa5, 0x34,
0x64, 0xdd, 0x24, 0xe6, 0x25, 0x2f, 0xac, 0xbe, 0x4d, 0x6c, 0x6f, 0xab, 0x97, 0xbc, 0x0d, 0xdf, 0xcf, 0x51, 0xda, 0x6d, 0xc5, 0x9d, 0x14, 0xe7, 0xd9, 0x0a, 0x71, 0x1c, 0x0e, 0xbf, 0x5c, 0x70,
0x27, 0x59, 0x38, 0xc2, 0xad, 0x75, 0xc0, 0x18, 0x18, 0x2a, 0x31, 0x10, 0xc2, 0x63, 0xc9, 0x17, 0xd8, 0xbc, 0x78, 0x4f, 0xce, 0xe5, 0x7f, 0x3c, 0x78, 0xda, 0xe1, 0x92, 0xac, 0x9b, 0xc4, 0xbc,
0x9b, 0xac, 0x40, 0x50, 0x2e, 0x4e, 0x44, 0x46, 0x2f, 0x5b, 0xf4, 0x32, 0x87, 0xfd, 0x1e, 0x2c, 0xe4, 0x85, 0xd5, 0xaf, 0x98, 0xed, 0x6d, 0xcd, 0x92, 0xb7, 0xe1, 0x7a, 0x92, 0x85, 0x63, 0x3c,
0xa2, 0xf7, 0x31, 0x91, 0x29, 0x9f, 0xd3, 0x20, 0x9e, 0x39, 0x4c, 0x62, 0x2e, 0x85, 0x25, 0xb7, 0x5a, 0x07, 0x8c, 0x81, 0xa1, 0xd2, 0x06, 0x21, 0xbc, 0x96, 0x7c, 0xb1, 0xcd, 0x0a, 0x04, 0xe5,
0x6b, 0x32, 0x03, 0x83, 0xb6, 0x79, 0x4a, 0x8b, 0xfb, 0x76, 0x9a, 0x4c, 0x27, 0x4f, 0x94, 0x1f, 0xe2, 0x44, 0x64, 0xb4, 0xd8, 0xa1, 0xc5, 0x1c, 0xf6, 0x07, 0xb0, 0x88, 0xde, 0xc7, 0x44, 0xa6,
0xf3, 0xfc, 0x24, 0x43, 0x4d, 0xe5, 0xa7, 0xab, 0xa3, 0x8c, 0x8a, 0x2f, 0xe5, 0xef, 0x42, 0x65, 0x7c, 0x4e, 0x83, 0x78, 0xe7, 0x28, 0x89, 0xb9, 0x14, 0x96, 0xdc, 0xae, 0xcd, 0x0c, 0x0c, 0xda,
0x06, 0x03, 0x13, 0xfc, 0xcb, 0xe5, 0xf2, 0x0b, 0xc9, 0x0e, 0x5b, 0xb0, 0x54, 0x58, 0x47, 0xf3, 0xe6, 0x29, 0x2d, 0xee, 0x5b, 0x69, 0x32, 0x9b, 0x3e, 0x51, 0x7e, 0xcc, 0xf3, 0x93, 0x0c, 0x35,
0x6c, 0xa2, 0xae, 0xc1, 0xb9, 0xe9, 0xb9, 0xad, 0x99, 0xe1, 0xbe, 0xe8, 0x56, 0x17, 0x86, 0xb4, 0x95, 0x9f, 0x2e, 0x8e, 0x32, 0x2a, 0xfa, 0x94, 0xbf, 0x0b, 0x95, 0x19, 0x0c, 0x4c, 0xf0, 0x6f,
0xed, 0x92, 0xb4, 0x9f, 0x78, 0xb0, 0xe9, 0x78, 0xa2, 0x69, 0x9a, 0xaa, 0xa8, 0xdf, 0x75, 0xa2, 0x97, 0xcb, 0xcf, 0x25, 0x3b, 0x6c, 0xc0, 0x52, 0x61, 0x1d, 0xcd, 0xb3, 0x89, 0xba, 0x04, 0xe7,
0x7e, 0xd3, 0x71, 0x79, 0x63, 0x7d, 0x1e, 0xf6, 0x3b, 0x56, 0xd8, 0x57, 0xae, 0xb0, 0xe2, 0xea, 0xa6, 0xe7, 0x76, 0xe6, 0x86, 0xfb, 0xa2, 0x5b, 0xd5, 0x18, 0xd2, 0x76, 0x4b, 0xd2, 0x7e, 0xec,
0x35, 0x37, 0xf2, 0xe7, 0x2d, 0xc9, 0xc3, 0xea, 0xa7, 0x1e, 0xac, 0x33, 0xfe, 0x28, 0xaf, 0x14, 0xc1, 0xba, 0xe3, 0x89, 0xa6, 0x69, 0xaa, 0xa2, 0x7e, 0xcb, 0x89, 0xfa, 0x75, 0xc7, 0xe5, 0x8d,
0x28, 0x45, 0xc4, 0xa7, 0xc9, 0x6c, 0x0f, 0x8b, 0xf4, 0x05, 0x64, 0xde, 0xb8, 0x75, 0x43, 0xd8, 0xfd, 0x79, 0xd8, 0xdf, 0xb2, 0xc2, 0xbe, 0x72, 0x87, 0x15, 0x57, 0xaf, 0xb9, 0x91, 0x5f, 0xb7,
0x59, 0x97, 0x8e, 0x95, 0x46, 0x9b, 0x6e, 0x1a, 0xdd, 0x87, 0x0d, 0xc6, 0xc5, 0xc4, 0x62, 0x44, 0x25, 0x0f, 0xab, 0x9f, 0x79, 0x70, 0x8d, 0xf1, 0x47, 0x79, 0xa5, 0x40, 0x29, 0x22, 0x3e, 0x4e,
0x5a, 0xf9, 0xff, 0xa1, 0x1e, 0x0d, 0xe5, 0x9d, 0x3a, 0x27, 0x9d, 0x21, 0x4d, 0xf0, 0x36, 0xe6, 0xe6, 0x7b, 0x58, 0xa4, 0x1f, 0x20, 0xf3, 0xc5, 0x6d, 0x1a, 0xc2, 0xce, 0x7b, 0x74, 0xac, 0x34,
0x08, 0x67, 0x13, 0x12, 0x5b, 0xf8, 0xb7, 0xcd, 0x5d, 0xe6, 0xa9, 0x86, 0x36, 0x9a, 0xc8, 0xbb, 0xda, 0x76, 0xd3, 0xe8, 0x0e, 0xac, 0x31, 0x2e, 0xa6, 0x16, 0x23, 0xd2, 0xca, 0x5f, 0x84, 0x66,
0x6e, 0x2f, 0x8a, 0x87, 0x87, 0x51, 0xcc, 0xd3, 0xfd, 0xf1, 0x90, 0xfc, 0x22, 0x8a, 0x87, 0x6f, 0x34, 0x92, 0x6f, 0x6a, 0x4d, 0x3a, 0x43, 0x9a, 0xe0, 0x2d, 0xcc, 0x11, 0xce, 0x21, 0x24, 0xb6,
0x52, 0x8f, 0xa4, 0xea, 0x57, 0x03, 0x43, 0xf2, 0x45, 0xf1, 0x70, 0x1f, 0xdd, 0x4f, 0x15, 0x4f, 0xf0, 0x6f, 0x9a, 0xa7, 0xd4, 0xa9, 0x86, 0x0e, 0x9a, 0xca, 0xb7, 0x6e, 0x3b, 0x8a, 0x47, 0xfb,
0x05, 0xa2, 0xc8, 0x3e, 0x78, 0x9e, 0x9d, 0x7d, 0x10, 0x13, 0xfc, 0xd9, 0x83, 0x35, 0xeb, 0x48, 0x51, 0xcc, 0xd3, 0x9d, 0xc9, 0x88, 0xfc, 0x22, 0x8a, 0x47, 0x6f, 0x50, 0x6f, 0xa6, 0xea, 0x66,
0xb2, 0xc2, 0x8c, 0x62, 0x00, 0xb7, 0x3d, 0x32, 0x23, 0xc9, 0xc0, 0xd8, 0x7c, 0xd4, 0xe7, 0xf3, 0x03, 0x43, 0xf2, 0x45, 0xf1, 0x68, 0x07, 0xdd, 0x4f, 0x15, 0x4f, 0x05, 0xa2, 0xc8, 0x3e, 0x78,
0xd1, 0x70, 0xf9, 0xc8, 0x2b, 0xd2, 0xe3, 0x68, 0xcc, 0x55, 0x44, 0x15, 0x08, 0x8c, 0x38, 0x59, 0x9f, 0x9d, 0x7d, 0x10, 0x13, 0xfc, 0xc5, 0x83, 0xab, 0xd6, 0x95, 0x64, 0x85, 0x39, 0xc5, 0x00,
0x9e, 0xca, 0xc0, 0x51, 0x75, 0x93, 0x81, 0x0a, 0x7e, 0xee, 0x41, 0xcf, 0x88, 0x8e, 0xab, 0xc5, 0x1e, 0x7b, 0x60, 0x46, 0x92, 0x81, 0xb1, 0xf9, 0x68, 0xd6, 0xf3, 0xd1, 0x72, 0xf9, 0xc8, 0x2b,
0xb9, 0x6d, 0x5d, 0x20, 0x3d, 0xc3, 0x32, 0xd6, 0x5a, 0xe5, 0xe5, 0xbb, 0xee, 0xed, 0x31, 0x7b, 0xd2, 0xc3, 0x68, 0xc2, 0x55, 0x44, 0x15, 0x08, 0x8c, 0x38, 0x59, 0x9e, 0xca, 0xc0, 0x51, 0x75,
0x41, 0xee, 0xe3, 0xf7, 0x65, 0x95, 0x8e, 0xe2, 0x21, 0xc5, 0x37, 0x63, 0x92, 0x92, 0x5a, 0x17, 0x93, 0x81, 0x0a, 0x7e, 0xe1, 0xc1, 0xc0, 0x88, 0x8e, 0x8b, 0xc5, 0xb9, 0x69, 0x3d, 0x20, 0x03,
0x52, 0x82, 0xe4, 0xa6, 0x40, 0xa0, 0xef, 0x8f, 0x71, 0x1b, 0x7d, 0x7f, 0x10, 0x10, 0x7c, 0xa7, 0xc3, 0x32, 0xd6, 0x5e, 0xe5, 0xe5, 0x5b, 0xee, 0xeb, 0x31, 0x7f, 0x43, 0xee, 0xe3, 0xf7, 0x65,
0xa8, 0x7f, 0x70, 0x9b, 0x07, 0x91, 0xc8, 0x66, 0x44, 0xc9, 0x0e, 0xb4, 0x68, 0x89, 0x2c, 0xf9, 0x95, 0x8e, 0xe2, 0x21, 0xc5, 0xb7, 0x62, 0x92, 0x92, 0x5a, 0x26, 0x52, 0x82, 0xe4, 0xa6, 0x40,
0x96, 0x76, 0x37, 0x1c, 0x77, 0x53, 0x5c, 0x30, 0x45, 0x15, 0x7c, 0x5c, 0xba, 0x80, 0xf5, 0x01, 0xa0, 0xef, 0x4f, 0xf0, 0x18, 0xfd, 0x7e, 0x10, 0x10, 0x7c, 0xb7, 0xa8, 0x7f, 0xf0, 0x98, 0x07,
0xea, 0x02, 0xd6, 0x25, 0x80, 0x57, 0x79, 0xa5, 0x6b, 0xe2, 0x72, 0x09, 0x50, 0x9b, 0x4f, 0x9f, 0x91, 0xc8, 0xe6, 0x44, 0xc9, 0x2d, 0xe8, 0xd0, 0x16, 0x59, 0xf2, 0x2d, 0x6d, 0xad, 0x39, 0xee,
0x6b, 0xe8, 0x31, 0x26, 0x01, 0x19, 0x37, 0x96, 0x78, 0x2f, 0x41, 0x63, 0x14, 0x89, 0xec, 0xca, 0xa6, 0xb8, 0x60, 0x8a, 0x2a, 0xf8, 0xa8, 0xf4, 0x00, 0xeb, 0x0b, 0xd4, 0x03, 0xac, 0x4b, 0x00,
0x73, 0x91, 0x08, 0x4d, 0xa3, 0x9b, 0x60, 0x29, 0xf6, 0x1c, 0xd3, 0x28, 0xc2, 0xe0, 0x27, 0xda, 0xaf, 0xf2, 0x49, 0xd7, 0xc4, 0xe5, 0x12, 0xa0, 0x51, 0x4f, 0x9f, 0x6b, 0xe8, 0x31, 0x26, 0x01,
0xeb, 0xd1, 0x83, 0x76, 0x0f, 0xc3, 0x28, 0x3e, 0x0c, 0x27, 0x46, 0x66, 0xf6, 0x66, 0x77, 0x4b, 0x19, 0x37, 0x96, 0x78, 0x2f, 0x41, 0x6b, 0x1c, 0x89, 0xec, 0xc2, 0x7b, 0x91, 0x08, 0x4d, 0xa3,
0x35, 0x9d, 0x41, 0xaa, 0xbb, 0xa5, 0xfa, 0xdc, 0x6e, 0xa9, 0x61, 0x77, 0x85, 0xc1, 0x3d, 0x59, 0x9b, 0x6f, 0x29, 0x76, 0x8d, 0x69, 0x14, 0x61, 0xf0, 0x53, 0xed, 0xf5, 0xe8, 0x41, 0x5b, 0xfb,
0xcf, 0x17, 0x6c, 0x90, 0xbb, 0xee, 0x40, 0x33, 0xca, 0xf8, 0x58, 0x67, 0x0d, 0x4b, 0x1e, 0x93, 0x61, 0x14, 0xef, 0x87, 0x53, 0x23, 0x33, 0x7b, 0xf3, 0xbb, 0xa5, 0x86, 0xce, 0x20, 0xd5, 0xdd,
0x61, 0x26, 0xc9, 0x82, 0xbf, 0xd7, 0xe5, 0x3d, 0x98, 0xe7, 0x1e, 0x15, 0x91, 0x2f, 0x40, 0x17, 0x52, 0xb3, 0xb6, 0x5b, 0x6a, 0xd9, 0xdd, 0x68, 0x70, 0x4f, 0xd6, 0xf3, 0x05, 0x1b, 0xe4, 0xae,
0x4f, 0x2a, 0xba, 0x21, 0x8f, 0x9a, 0x35, 0x1b, 0x89, 0x7d, 0x67, 0x81, 0x30, 0x5b, 0x30, 0x17, 0xb7, 0xa0, 0x1d, 0x65, 0x7c, 0xa2, 0xb3, 0x86, 0x25, 0x8f, 0xc9, 0x30, 0x93, 0x64, 0xc1, 0x3f,
0x3d, 0xe3, 0xbe, 0x2c, 0xb4, 0xd6, 0xb0, 0xb4, 0x16, 0xc0, 0xf2, 0x24, 0xe5, 0xc5, 0xe1, 0xb2, 0x9b, 0xf2, 0x1d, 0xcc, 0x73, 0x8f, 0x8a, 0xc8, 0x17, 0xa0, 0x8f, 0x37, 0x15, 0xdd, 0x90, 0x47,
0x53, 0xb4, 0x70, 0xb6, 0x66, 0x5b, 0x6e, 0x1f, 0x2a, 0x77, 0x40, 0x61, 0xb8, 0x6a, 0x87, 0xf5, 0xcd, 0x9a, 0x8d, 0xc4, 0x7e, 0xb7, 0x40, 0x98, 0x2d, 0x98, 0x8b, 0x9e, 0xf3, 0x5e, 0x16, 0x5a,
0x0e, 0x39, 0x8e, 0x22, 0x2a, 0x27, 0x68, 0xcb, 0x1d, 0x72, 0x04, 0xea, 0x3e, 0xbb, 0xd8, 0x4f, 0x6b, 0x59, 0x5a, 0x0b, 0x60, 0x79, 0x9a, 0xf2, 0xe2, 0x72, 0xd9, 0x29, 0x5a, 0x38, 0x5b, 0xb3,
0xa6, 0x71, 0x26, 0xa8, 0x82, 0xee, 0xb2, 0x1c, 0x96, 0xef, 0xe4, 0xa4, 0xa6, 0x07, 0xb2, 0x8b, 0x1d, 0xb7, 0x0f, 0x95, 0x27, 0xa0, 0x30, 0x5c, 0xb5, 0xe1, 0xfa, 0x84, 0x1c, 0x47, 0x11, 0x95,
0xd5, 0x30, 0x56, 0x4e, 0xd9, 0x85, 0x9c, 0xf9, 0x2c, 0xd1, 0x50, 0x47, 0x83, 0xd4, 0x8a, 0xa2, 0x13, 0x74, 0xe5, 0x09, 0x39, 0x02, 0x75, 0x9f, 0x9d, 0xed, 0x24, 0xb3, 0x38, 0x13, 0x54, 0x41,
0x9a, 0x8f, 0xf5, 0xd2, 0x65, 0xa9, 0x53, 0x0b, 0x89, 0x9c, 0x2b, 0x84, 0xdc, 0xa4, 0x4b, 0x9b, 0xf7, 0x59, 0x0e, 0xcb, 0x35, 0x39, 0x21, 0x1a, 0x80, 0xec, 0x62, 0x35, 0x8c, 0x95, 0x53, 0x76,
0x58, 0x38, 0xff, 0x36, 0xac, 0xc5, 0x49, 0xbc, 0x4f, 0xbd, 0xfd, 0xb1, 0x66, 0x72, 0x85, 0x98, 0x26, 0x67, 0x4d, 0x4b, 0x34, 0x4c, 0xd2, 0x20, 0xb5, 0xa2, 0xa8, 0xe6, 0x43, 0xbd, 0x75, 0x59,
0x2c, 0xbf, 0x08, 0xf6, 0x60, 0xed, 0x88, 0x8f, 0x4e, 0x55, 0x47, 0x7d, 0x94, 0x85, 0x67, 0x5c, 0xea, 0xd4, 0x42, 0x22, 0xe7, 0x0a, 0x21, 0x0f, 0xe9, 0xd3, 0x21, 0x16, 0xce, 0xbf, 0x09, 0x57,
0xf8, 0x2f, 0xdb, 0x8e, 0xa2, 0x03, 0xc5, 0x25, 0xd4, 0x7e, 0xf2, 0x00, 0x6e, 0xb8, 0xaf, 0x30, 0xe3, 0x24, 0xde, 0xa1, 0x36, 0xff, 0x50, 0x33, 0xb9, 0x42, 0x4c, 0x96, 0x17, 0x82, 0x6d, 0xb8,
0xb3, 0x8a, 0x2c, 0x4c, 0xb3, 0xbe, 0xe9, 0xf8, 0x26, 0x0a, 0xed, 0xcb, 0xe3, 0xf0, 0x44, 0x95, 0x7a, 0xc0, 0xc7, 0xc7, 0xaa, 0xa3, 0x3e, 0xc8, 0xc2, 0x13, 0x2e, 0xfc, 0x97, 0x6d, 0x47, 0xd1,
0xb5, 0x5d, 0xa6, 0xa0, 0xe0, 0x6f, 0x1e, 0xac, 0xbb, 0xdb, 0x91, 0xfb, 0xce, 0x2f, 0xbf, 0xba, 0x81, 0xe2, 0x12, 0x6a, 0x3f, 0x79, 0x00, 0x57, 0xdc, 0x25, 0xcc, 0xac, 0x22, 0x0b, 0xd3, 0x6c,
0xf9, 0xc5, 0xfc, 0x32, 0x34, 0x05, 0x2e, 0x72, 0x3a, 0x8c, 0x32, 0xf7, 0x44, 0x65, 0xd5, 0x54, 0xd7, 0x74, 0x7c, 0x13, 0x85, 0xf6, 0xe5, 0x71, 0x78, 0xa4, 0xca, 0xda, 0x3e, 0x53, 0x50, 0xf0,
0x0d, 0xa7, 0xa6, 0xba, 0x05, 0xc0, 0x2f, 0xf8, 0xc0, 0x9e, 0x67, 0x15, 0x98, 0xcf, 0xdc, 0xaf, 0x77, 0x0f, 0xae, 0xb9, 0xc7, 0x91, 0xfb, 0xd6, 0x97, 0x5f, 0xfd, 0xfc, 0x61, 0x7e, 0x19, 0xda,
0x05, 0x1c, 0x36, 0x1e, 0x24, 0x83, 0x70, 0xa4, 0x99, 0x29, 0xa4, 0xbb, 0xab, 0xb9, 0xf6, 0xac, 0x02, 0x37, 0x39, 0x1d, 0x46, 0x99, 0x7b, 0xa2, 0xb2, 0x6a, 0xaa, 0x96, 0x53, 0x53, 0xdd, 0x00,
0x2e, 0xa2, 0x4a, 0x13, 0x9a, 0x73, 0xf2, 0xa6, 0x83, 0x78, 0xc8, 0x2f, 0x54, 0xf6, 0xd0, 0x60, 0xe0, 0x67, 0x7c, 0x68, 0xcf, 0xd1, 0x0a, 0xcc, 0xa7, 0xee, 0xd7, 0x02, 0x0e, 0x6b, 0x0f, 0x92,
0xf0, 0x3a, 0xac, 0xc8, 0xf2, 0x0b, 0x39, 0xa8, 0x54, 0x5e, 0x3e, 0x47, 0xa8, 0x19, 0x73, 0x84, 0x61, 0x38, 0xd6, 0xcc, 0x14, 0xd2, 0xdd, 0xd1, 0x5c, 0x7b, 0x56, 0x17, 0x51, 0xa5, 0x09, 0xcd,
0x20, 0x80, 0x1b, 0x72, 0xdd, 0x7e, 0x18, 0x0f, 0xf8, 0xa8, 0x6a, 0x65, 0xf0, 0xa9, 0x9a, 0x12, 0x39, 0x79, 0xd3, 0x5e, 0x3c, 0xe2, 0x67, 0x2a, 0x7b, 0x68, 0x30, 0xb8, 0x0b, 0x2b, 0xb2, 0xfc,
0x11, 0x3b, 0x57, 0xd5, 0xef, 0xd9, 0xa5, 0xae, 0xdf, 0xb3, 0x4b, 0xd4, 0x96, 0x14, 0x11, 0xe6, 0x42, 0x0e, 0x2a, 0x95, 0x97, 0xcf, 0x11, 0x1a, 0xc6, 0x1c, 0x21, 0x08, 0xe0, 0x8a, 0xdc, 0xb7,
0x1a, 0xa6, 0xbf, 0xa0, 0x05, 0x7c, 0x09, 0x1a, 0xa8, 0xb6, 0xde, 0x12, 0xd1, 0xdf, 0x54, 0xf4, 0x13, 0xc6, 0x43, 0x3e, 0xae, 0xda, 0x19, 0x7c, 0xa2, 0xa6, 0x44, 0xc4, 0xce, 0x45, 0xf5, 0x7b,
0xb6, 0x64, 0xfd, 0x05, 0x46, 0x44, 0xd4, 0x8a, 0x12, 0xd7, 0x14, 0x3a, 0xc5, 0xf6, 0xae, 0x40, 0x76, 0xae, 0xeb, 0xf7, 0xec, 0x1c, 0xb5, 0x25, 0x45, 0x84, 0x5a, 0xc3, 0xec, 0x2e, 0x68, 0x01,
0xfd, 0x05, 0xa6, 0x08, 0xf7, 0x16, 0x95, 0x12, 0x82, 0x1f, 0x17, 0x35, 0xb0, 0x65, 0x19, 0x25, 0x5f, 0x82, 0x16, 0xaa, 0x6d, 0xb0, 0x44, 0xf4, 0xd7, 0x15, 0xbd, 0x2d, 0xd9, 0xee, 0x02, 0x23,
0xde, 0x1d, 0xeb, 0xbe, 0x9a, 0x6b, 0x9a, 0x52, 0x53, 0x58, 0xbb, 0x7a, 0x4d, 0x7e, 0x6f, 0x7d, 0x22, 0x6a, 0x45, 0x89, 0x6b, 0x0a, 0x9d, 0xe2, 0x78, 0x57, 0xa0, 0xdd, 0x05, 0xa6, 0x08, 0xb7,
0xea, 0xc1, 0x73, 0x55, 0x6c, 0xcc, 0xec, 0x0c, 0x73, 0x57, 0xaf, 0x5d, 0xcb, 0xd5, 0xed, 0x96, 0x17, 0x95, 0x12, 0x82, 0x9f, 0x14, 0x35, 0xb0, 0x65, 0x19, 0x25, 0xde, 0x6d, 0xeb, 0xbd, 0xaa,
0xb0, 0x3e, 0xbf, 0x25, 0x6c, 0xcc, 0x6b, 0x09, 0x9b, 0xb3, 0x5b, 0xc2, 0x96, 0xd5, 0x12, 0x06, 0x35, 0x4d, 0xa9, 0x29, 0x6c, 0x5c, 0xbc, 0x27, 0x7f, 0xb7, 0x3e, 0xf1, 0xe0, 0xb9, 0x2a, 0x36,
0x1f, 0xc3, 0xb3, 0x55, 0x22, 0x09, 0x55, 0x0a, 0xdc, 0xb6, 0x54, 0xdb, 0x9b, 0x21, 0x80, 0x28, 0xe6, 0x76, 0x86, 0xb9, 0xab, 0x37, 0x2e, 0xe5, 0xea, 0x76, 0x4b, 0xd8, 0xac, 0x6f, 0x09, 0x5b,
0x97, 0x4b, 0xb5, 0x2b, 0x16, 0xe4, 0x4a, 0xfd, 0xb5, 0x07, 0x3e, 0xe3, 0x8f, 0xde, 0x9b, 0xf2, 0x75, 0x2d, 0x61, 0x7b, 0x7e, 0x4b, 0xd8, 0xb1, 0x5a, 0xc2, 0xe0, 0x23, 0x78, 0xb6, 0x4a, 0x24,
0xf4, 0x12, 0xc9, 0x54, 0x8e, 0xb3, 0x47, 0xb7, 0x45, 0xf6, 0x70, 0x5b, 0x82, 0x75, 0x68, 0x0e, 0xa1, 0x4a, 0x81, 0x9b, 0x96, 0x6a, 0x07, 0x73, 0x04, 0x10, 0xe5, 0x72, 0xa9, 0x71, 0xc1, 0x86,
0x30, 0x55, 0x2a, 0x75, 0x49, 0x00, 0x35, 0x35, 0x8c, 0x52, 0x2e, 0x6b, 0x67, 0xa5, 0xa9, 0x1c, 0x5c, 0xa9, 0xbf, 0xf1, 0xc0, 0x67, 0xfc, 0xd1, 0xbb, 0x33, 0x9e, 0x9e, 0x23, 0x99, 0xca, 0x71,
0x61, 0x5c, 0x5d, 0x4d, 0xeb, 0xea, 0x5a, 0x87, 0x66, 0x44, 0xe1, 0x2a, 0x3b, 0x6a, 0x09, 0x04, 0xf6, 0xc8, 0xb8, 0xc8, 0x1e, 0x6e, 0x4b, 0x70, 0x0d, 0xda, 0x43, 0x4c, 0x95, 0x4a, 0x5d, 0x12,
0xef, 0x61, 0xb5, 0x32, 0x19, 0x5d, 0xba, 0x1c, 0xbe, 0x41, 0x57, 0x90, 0xf4, 0x11, 0x95, 0x89, 0x40, 0x4d, 0x8d, 0xa2, 0x94, 0xcb, 0xda, 0x59, 0x69, 0x2a, 0x47, 0x18, 0x4f, 0x57, 0xdb, 0x7a,
0xe7, 0xba, 0x51, 0x41, 0x1d, 0x7c, 0xcf, 0xf8, 0x96, 0xb1, 0xaf, 0xa6, 0xbc, 0x42, 0x97, 0xac, 0xba, 0xae, 0x41, 0x3b, 0xa2, 0x70, 0x95, 0x1d, 0xb5, 0x04, 0x82, 0x77, 0xb1, 0x5a, 0x99, 0x8e,
0x22, 0x3a, 0x8b, 0xd5, 0x95, 0x4d, 0xcf, 0x68, 0x58, 0x6a, 0x9d, 0x0f, 0x43, 0xd9, 0x6d, 0x2f, 0xcf, 0x5d, 0x0e, 0x5f, 0xa7, 0x27, 0x48, 0xfa, 0x88, 0xca, 0xc4, 0xb5, 0x6e, 0x54, 0x50, 0x07,
0xb3, 0x1c, 0x2e, 0x7a, 0xec, 0xba, 0x31, 0x03, 0x0c, 0x7e, 0x68, 0x7c, 0x7f, 0x90, 0xfb, 0xab, 0xdf, 0x37, 0xfe, 0x43, 0xd9, 0x51, 0xd3, 0x65, 0xa1, 0x4b, 0x56, 0x11, 0x9d, 0xc4, 0xea, 0xc9,
0xa6, 0x61, 0xd7, 0xd2, 0xaa, 0xdd, 0x99, 0x38, 0x65, 0x44, 0xae, 0xf1, 0x3b, 0x50, 0x3f, 0x19, 0xa6, 0xdf, 0x68, 0x58, 0x6a, 0x9d, 0xf7, 0x43, 0xd9, 0x6d, 0x2f, 0xb3, 0x1c, 0x2e, 0x7a, 0xec,
0x09, 0x65, 0xd0, 0xd2, 0x84, 0xde, 0x62, 0x9f, 0x21, 0x65, 0x90, 0xc9, 0x51, 0x23, 0xbd, 0xa6, 0xa6, 0x31, 0x03, 0x0c, 0x7e, 0x64, 0xfc, 0xef, 0x21, 0xcf, 0x57, 0x4d, 0xc3, 0x96, 0xa5, 0x55,
0x22, 0xec, 0x09, 0x0e, 0xdf, 0x86, 0xd5, 0x48, 0x18, 0xea, 0x54, 0xb7, 0x49, 0x9b, 0xb9, 0xe8, 0xbb, 0x33, 0x71, 0xca, 0x88, 0x5c, 0xe3, 0xb7, 0xa1, 0x79, 0x34, 0x16, 0xca, 0xa0, 0xa5, 0x7f,
0xe0, 0x97, 0x1e, 0xf8, 0xfb, 0xb8, 0xcb, 0x9b, 0x42, 0xf0, 0xec, 0x38, 0x0d, 0x63, 0x71, 0xca, 0x06, 0x2c, 0xf6, 0x19, 0x52, 0x06, 0x99, 0x1c, 0x35, 0xd2, 0x32, 0x15, 0x61, 0x4f, 0x70, 0xf9,
0x53, 0xf4, 0x84, 0x10, 0x11, 0xf7, 0x2f, 0xf8, 0x40, 0x97, 0xde, 0x39, 0x02, 0xaf, 0x41, 0x02, 0x26, 0xac, 0x46, 0xc2, 0x50, 0xa7, 0x7a, 0x4d, 0xba, 0xcc, 0x45, 0x07, 0xbf, 0xf2, 0xc0, 0xdf,
0x8e, 0x2e, 0xc7, 0x27, 0xc9, 0x48, 0xb9, 0x95, 0x89, 0x42, 0x5f, 0x09, 0xc7, 0xb9, 0x83, 0xd5, 0xc1, 0x53, 0xde, 0x10, 0x82, 0x67, 0x87, 0x69, 0x18, 0x8b, 0x63, 0x9e, 0xa2, 0x27, 0x84, 0x88,
0x99, 0x82, 0x10, 0x9f, 0x25, 0xc6, 0xa5, 0xa4, 0x20, 0x34, 0x60, 0xac, 0x23, 0xb0, 0xc3, 0xe8, 0xb8, 0x7f, 0xc6, 0x87, 0xba, 0xf4, 0xce, 0x11, 0xf8, 0x0c, 0x12, 0x70, 0x70, 0x3e, 0x39, 0x4a,
0x39, 0xf8, 0x4f, 0xcb, 0x18, 0xdc, 0x2b, 0x65, 0xbc, 0x8e, 0x3d, 0x3a, 0xaa, 0x4e, 0x29, 0xe3, 0xc6, 0xca, 0xad, 0x4c, 0x14, 0xfa, 0x4a, 0x38, 0xc9, 0x1d, 0xac, 0xc9, 0x14, 0x84, 0xf8, 0x2c,
0xb9, 0x6a, 0xc5, 0x4a, 0x6a, 0xca, 0x89, 0x04, 0xfb, 0xaf, 0xea, 0x66, 0xa1, 0x3c, 0xcd, 0x72, 0x31, 0x1e, 0x25, 0x05, 0xa1, 0x01, 0x63, 0x1d, 0x81, 0x3d, 0x46, 0xbf, 0x83, 0xff, 0x76, 0x8c,
0x15, 0x8e, 0x89, 0x9a, 0x68, 0xfd, 0xaf, 0x41, 0x37, 0x34, 0xb5, 0xa2, 0x5a, 0x76, 0x9d, 0xb1, 0xc1, 0xbd, 0x52, 0xc6, 0x5d, 0xec, 0xd1, 0x51, 0x75, 0x4a, 0x19, 0xcf, 0x55, 0x2b, 0x56, 0x52,
0x49, 0x63, 0x42, 0xbf, 0xec, 0x2f, 0x30, 0x9b, 0x3a, 0x5f, 0xfe, 0xed, 0x28, 0x3b, 0x1f, 0xa6, 0x53, 0x4e, 0x24, 0xd8, 0x7f, 0x55, 0x37, 0x0b, 0xe5, 0x69, 0x96, 0xab, 0x70, 0x4c, 0xd4, 0x44,
0xe1, 0x63, 0x12, 0xce, 0x5d, 0xae, 0x5f, 0xe6, 0xcb, 0x35, 0xc2, 0x7f, 0x15, 0xda, 0x99, 0x3e, 0xeb, 0x7f, 0x0d, 0xfa, 0xa1, 0xa9, 0x15, 0xd5, 0xb2, 0xeb, 0x8c, 0x4d, 0x1a, 0x13, 0x7a, 0x71,
0xb8, 0x35, 0xff, 0xe0, 0x9c, 0x10, 0x17, 0x3d, 0xd6, 0xc7, 0x2d, 0xce, 0x3f, 0x2e, 0x27, 0xf4, 0x77, 0x81, 0xd9, 0xd4, 0xf9, 0xf6, 0xef, 0x44, 0xd9, 0xe9, 0x28, 0x0d, 0x1f, 0x93, 0x70, 0xee,
0xef, 0xc3, 0x8a, 0xde, 0xe0, 0x38, 0x21, 0x8b, 0xb7, 0x2d, 0x2d, 0xd9, 0xe7, 0x49, 0x92, 0xfe, 0x76, 0xbd, 0x98, 0x6f, 0xd7, 0x08, 0xff, 0x55, 0xe8, 0x66, 0xfa, 0xe2, 0x4e, 0xfd, 0xc5, 0x39,
0x02, 0x73, 0x16, 0xf9, 0x5f, 0x05, 0x88, 0xf3, 0xb9, 0x2a, 0x15, 0x90, 0xf3, 0x26, 0xa7, 0xfd, 0x21, 0x6e, 0x7a, 0xac, 0xaf, 0x5b, 0xac, 0xbf, 0x2e, 0x27, 0xf4, 0xef, 0xc3, 0x8a, 0x3e, 0xe0,
0x05, 0x66, 0x90, 0xfb, 0x6f, 0xc1, 0x6a, 0x6c, 0xcf, 0x58, 0xd4, 0x7d, 0x3a, 0x67, 0x0a, 0xd3, 0x30, 0x21, 0x8b, 0x77, 0x2d, 0x2d, 0xd9, 0xf7, 0x49, 0x92, 0xdd, 0x05, 0xe6, 0x6c, 0xf2, 0xbf,
0x5f, 0x60, 0xee, 0x22, 0x7f, 0x0f, 0x56, 0x85, 0x4e, 0x36, 0x6a, 0x1f, 0x79, 0xcf, 0x9a, 0xed, 0x0a, 0x10, 0xe7, 0x73, 0x55, 0x2a, 0x20, 0xeb, 0x26, 0xa7, 0xbb, 0x0b, 0xcc, 0x20, 0xf7, 0xdf,
0x9d, 0xf1, 0x16, 0xf7, 0x70, 0x16, 0xf8, 0xef, 0x80, 0x3f, 0x28, 0x85, 0x84, 0xba, 0x7f, 0xb5, 0x84, 0xd5, 0xd8, 0x9e, 0xb1, 0xa8, 0xf7, 0xb4, 0x66, 0x0a, 0xb3, 0xbb, 0xc0, 0xdc, 0x4d, 0xfe,
0x40, 0xe5, 0x98, 0xe9, 0x2f, 0xb0, 0x8a, 0x65, 0xfe, 0xd7, 0xa1, 0x3b, 0x31, 0x5b, 0xab, 0x5e, 0x36, 0xac, 0x0a, 0x9d, 0x6c, 0xd4, 0x39, 0xf2, 0x9d, 0x35, 0xdb, 0x3b, 0x63, 0x15, 0xcf, 0x70,
0xb7, 0xd4, 0xa6, 0x99, 0x03, 0x0c, 0xf4, 0x03, 0x8b, 0xde, 0xff, 0x00, 0x36, 0x8d, 0x0f, 0x78, 0x36, 0xf8, 0x6f, 0x83, 0x3f, 0x2c, 0x85, 0x84, 0x7a, 0x7f, 0xb5, 0x40, 0xe5, 0x98, 0xd9, 0x5d,
0x8e, 0x0a, 0xa8, 0x04, 0xbe, 0x42, 0xcd, 0x73, 0x96, 0x1b, 0xc5, 0x4c, 0x13, 0x8b, 0x99, 0xa2, 0x60, 0x15, 0xdb, 0xfc, 0x6f, 0x40, 0x7f, 0x6a, 0xb6, 0x56, 0x83, 0x7e, 0xa9, 0x4d, 0x33, 0x07,
0x76, 0xf8, 0xc4, 0x83, 0x0d, 0xa3, 0xdd, 0x35, 0x62, 0x6b, 0xd6, 0xec, 0xcc, 0xa8, 0x5a, 0xaf, 0x18, 0xe8, 0x07, 0x16, 0xbd, 0xff, 0x3e, 0xac, 0x1b, 0x7f, 0x1c, 0x3a, 0x2a, 0xa0, 0x12, 0xf8,
0x97, 0xa4, 0x5e, 0xb1, 0x66, 0x67, 0xa5, 0x48, 0xb6, 0xbe, 0x4a, 0xca, 0x8b, 0xf2, 0x75, 0x77, 0x02, 0x35, 0xd7, 0x6c, 0x37, 0x8a, 0x99, 0x36, 0x16, 0x33, 0x45, 0xed, 0xf0, 0xb1, 0x07, 0x6b,
0x7a, 0x36, 0x7f, 0x51, 0x7e, 0x59, 0xbe, 0x63, 0x0d, 0xff, 0x8b, 0x80, 0xff, 0x3c, 0xb9, 0x35, 0x46, 0xbb, 0x6b, 0xc4, 0xd6, 0xbc, 0xd9, 0x99, 0x51, 0xb5, 0x5e, 0x2e, 0x49, 0xbd, 0x62, 0xcd,
0xf8, 0x51, 0x03, 0x6f, 0x36, 0x7b, 0x37, 0x2a, 0x63, 0xec, 0x3a, 0xc4, 0x2b, 0xd5, 0x21, 0x5b, 0xce, 0x4a, 0x91, 0x6c, 0xfd, 0x1b, 0x2a, 0x1f, 0xca, 0xbb, 0xee, 0xf4, 0xac, 0x7e, 0x53, 0xfe,
0xb0, 0x44, 0x90, 0x54, 0xa3, 0x52, 0xba, 0x89, 0xf2, 0x5f, 0x84, 0x15, 0xac, 0x3d, 0x8e, 0xc2, 0x58, 0xbe, 0x6d, 0x0d, 0xff, 0x8b, 0x80, 0xff, 0x2c, 0xb9, 0x35, 0xf8, 0x71, 0x0b, 0x5f, 0x36,
0x31, 0x57, 0x44, 0xf2, 0x7a, 0x76, 0xb0, 0x45, 0x61, 0xda, 0xa8, 0x6e, 0x2d, 0x9b, 0x6e, 0x43, 0xfb, 0x34, 0x2a, 0x63, 0xec, 0x3a, 0xc4, 0x2b, 0xd5, 0x21, 0x1b, 0xb0, 0x44, 0x90, 0x54, 0xa3,
0x5e, 0x34, 0x7d, 0xad, 0x79, 0x4d, 0xdf, 0xe2, 0x9c, 0xa6, 0xaf, 0xed, 0x34, 0x7d, 0x56, 0x33, 0x52, 0xba, 0x89, 0xf2, 0x5f, 0x84, 0x15, 0xac, 0x3d, 0x0e, 0xc2, 0x09, 0x57, 0x44, 0xf2, 0x79,
0xda, 0x71, 0x9b, 0x51, 0xa3, 0x25, 0x84, 0x2b, 0x5a, 0xc2, 0xa5, 0xeb, 0xb4, 0x84, 0xcb, 0x15, 0x76, 0xb0, 0x45, 0x61, 0xda, 0xaa, 0x6e, 0x2d, 0xdb, 0x6e, 0x43, 0x5e, 0x34, 0x7d, 0x9d, 0xba,
0x2d, 0x61, 0xa9, 0x61, 0xef, 0x5e, 0xb3, 0x61, 0x5f, 0xa9, 0x6e, 0xd8, 0xb7, 0x61, 0x95, 0x3e, 0xa6, 0x6f, 0xb1, 0xa6, 0xe9, 0xeb, 0x3a, 0x4d, 0x9f, 0xd5, 0x8c, 0xf6, 0xdc, 0x66, 0xd4, 0x68,
0xa3, 0xde, 0x2f, 0x7a, 0xa3, 0x55, 0x49, 0xe9, 0xa0, 0x83, 0xef, 0x97, 0x63, 0x83, 0xf1, 0x41, 0x09, 0xe1, 0x82, 0x96, 0x70, 0xe9, 0x32, 0x2d, 0xe1, 0x72, 0x45, 0x4b, 0x58, 0x6a, 0xd8, 0xfb,
0x92, 0x0e, 0xbf, 0xa8, 0xd8, 0x08, 0xfe, 0x0f, 0x96, 0xf2, 0xd7, 0xc7, 0x17, 0x74, 0x6d, 0x5e, 0x97, 0x6c, 0xd8, 0x57, 0xaa, 0x1b, 0xf6, 0x4d, 0x58, 0xa5, 0xbf, 0x51, 0xef, 0x17, 0xbd, 0xd1,
0xe4, 0x43, 0x09, 0xbc, 0x36, 0x09, 0x92, 0xa3, 0xd4, 0x62, 0x2e, 0x7c, 0x8c, 0x7e, 0xe0, 0x8e, 0xaa, 0xa4, 0x74, 0xd0, 0xc1, 0x0f, 0xca, 0xb1, 0xc1, 0xf8, 0x30, 0x49, 0x47, 0x9f, 0x57, 0x6c,
0x1f, 0xae, 0xf3, 0x89, 0x3b, 0xf8, 0x5d, 0x0d, 0xd6, 0xac, 0xa1, 0xec, 0xff, 0x96, 0x47, 0x77, 0x04, 0x5f, 0x80, 0xa5, 0x7c, 0xf9, 0xf0, 0x8c, 0x9e, 0xcd, 0xb3, 0x7c, 0x28, 0x81, 0xcf, 0x26,
0x3e, 0xaf, 0x47, 0x77, 0x0c, 0x8f, 0xae, 0xb0, 0x7f, 0xa7, 0xda, 0xfe, 0x6f, 0xc3, 0x53, 0x96, 0x41, 0x72, 0x94, 0x5a, 0xcc, 0x85, 0x0f, 0xd1, 0x0f, 0xdc, 0xf1, 0xc3, 0x65, 0xfe, 0xe2, 0x0e,
0xb2, 0x48, 0xef, 0x98, 0xd0, 0x5a, 0xc4, 0xb7, 0x3b, 0x8a, 0x2a, 0x29, 0x96, 0x29, 0x3a, 0x99, 0x7e, 0xdf, 0x80, 0xab, 0xd6, 0x50, 0xf6, 0xff, 0xcb, 0xa3, 0x7b, 0x9f, 0xd5, 0xa3, 0x7b, 0x86,
0x98, 0x5c, 0xfb, 0xa1, 0x0c, 0xd5, 0xd6, 0x2b, 0x8d, 0xd6, 0xac, 0x9f, 0x73, 0xfe, 0x52, 0x83, 0x47, 0x57, 0xd8, 0xbf, 0x57, 0x6d, 0xff, 0xb7, 0xe0, 0x29, 0x4b, 0x59, 0xa4, 0x77, 0x4c, 0x68,
0x95, 0xa2, 0x5e, 0xc2, 0x4b, 0x08, 0xdd, 0x11, 0xbb, 0x7e, 0xed, 0x8e, 0xf8, 0x4c, 0x29, 0x3f, 0x1d, 0xe2, 0xdb, 0x1d, 0x45, 0x95, 0x14, 0xcb, 0x14, 0x9d, 0x4c, 0x4c, 0xae, 0xfd, 0x50, 0x86,
0xd1, 0xad, 0x40, 0x96, 0xa0, 0x91, 0xa3, 0xbc, 0x2e, 0x20, 0xf3, 0xb4, 0x99, 0x81, 0x31, 0x7c, 0x6a, 0xeb, 0x95, 0x46, 0x6b, 0xd6, 0x47, 0x41, 0x7f, 0x6d, 0xc0, 0x4a, 0x51, 0x2f, 0xe1, 0x23,
0xaf, 0x61, 0xfa, 0x9e, 0x51, 0xe2, 0x35, 0xad, 0x12, 0xcf, 0x87, 0x06, 0xc7, 0x1a, 0x42, 0xda, 0x84, 0xee, 0x88, 0x5d, 0xbf, 0x76, 0x47, 0xfc, 0x4d, 0x29, 0x3f, 0xd1, 0xad, 0x40, 0x96, 0xa0,
0x85, 0x9e, 0xa9, 0x2d, 0x91, 0xb5, 0xa2, 0xfc, 0xb4, 0xa6, 0x20, 0x14, 0x48, 0x0a, 0x7e, 0x39, 0x91, 0xa3, 0xbc, 0x2e, 0x20, 0xf3, 0x74, 0x99, 0x81, 0x31, 0x7c, 0xaf, 0x65, 0xfa, 0x9e, 0x51,
0xe1, 0x64, 0x8f, 0x2e, 0x2b, 0x10, 0x86, 0xf9, 0xc1, 0x32, 0x3f, 0xfd, 0xba, 0x80, 0x6e, 0x83, 0xe2, 0xb5, 0xad, 0x12, 0xcf, 0x87, 0x16, 0xc7, 0x1a, 0x42, 0xda, 0x85, 0x7e, 0x53, 0x5b, 0x22,
0xba, 0x54, 0x96, 0xba, 0x49, 0x14, 0x25, 0x3c, 0x7d, 0x94, 0x0f, 0xd3, 0x50, 0x51, 0x6d, 0xc8, 0x6b, 0x45, 0xf9, 0xd7, 0x9a, 0x82, 0x50, 0x20, 0x29, 0xf8, 0xf9, 0x94, 0x93, 0x3d, 0xfa, 0xac,
0x59, 0x47, 0x81, 0xc1, 0x44, 0x25, 0xa6, 0x83, 0x01, 0x17, 0xa2, 0xf7, 0x34, 0x89, 0xae, 0xc1, 0x40, 0x18, 0xe6, 0x07, 0xcb, 0xfc, 0xf4, 0xe9, 0x02, 0xba, 0x0d, 0xea, 0x52, 0x59, 0xea, 0x3a,
0xe0, 0xaf, 0x9e, 0x1c, 0x25, 0xd3, 0x64, 0xe3, 0xde, 0x09, 0x65, 0x8a, 0x99, 0x43, 0x4f, 0x73, 0x51, 0x94, 0xf0, 0xf4, 0xa7, 0x7c, 0x98, 0x86, 0x8a, 0x6a, 0x4d, 0xce, 0x3a, 0x0a, 0x0c, 0x26,
0x6c, 0x59, 0x73, 0x7e, 0x01, 0xba, 0x6a, 0xe4, 0xf9, 0x22, 0xac, 0x4c, 0x42, 0xbc, 0xa7, 0x0e, 0x2a, 0x31, 0x1b, 0x0e, 0xb9, 0x10, 0x83, 0xa7, 0x49, 0x74, 0x0d, 0x06, 0x7f, 0xf3, 0xe4, 0x28,
0xcd, 0xc1, 0xe7, 0x32, 0x73, 0xb0, 0x57, 0x0c, 0xfd, 0x5f, 0x80, 0x7a, 0x76, 0x21, 0x7f, 0x95, 0x99, 0x26, 0x1b, 0xf7, 0x8e, 0x28, 0x53, 0xcc, 0x1d, 0x7a, 0x9a, 0x63, 0xcb, 0x86, 0xf3, 0xe9,
0x59, 0xda, 0xf5, 0x95, 0xe7, 0x1d, 0x17, 0xff, 0x8b, 0x31, 0x7c, 0x1d, 0xfc, 0x49, 0xfd, 0xd0, 0xd1, 0x45, 0x23, 0xcf, 0x17, 0x61, 0x65, 0x1a, 0xe2, 0x3b, 0xb5, 0x6f, 0x0e, 0x3e, 0x97, 0x99,
0x60, 0x0a, 0x45, 0x1d, 0xd4, 0x75, 0x05, 0xeb, 0x3c, 0xb1, 0x60, 0x9d, 0xcf, 0x28, 0xd8, 0x8d, 0x83, 0xbd, 0x60, 0xe8, 0xff, 0x02, 0x34, 0xb3, 0x33, 0xf9, 0x89, 0xce, 0xd2, 0x96, 0xaf, 0x3c,
0x42, 0xb0, 0x8e, 0x14, 0x22, 0x91, 0x7d, 0xda, 0xde, 0x48, 0x1c, 0x45, 0x67, 0xf1, 0xd1, 0x74, 0xef, 0xb0, 0xf8, 0x4e, 0x8d, 0xe1, 0x72, 0xf0, 0x67, 0xf5, 0x41, 0x83, 0x29, 0x14, 0x75, 0x50,
0xac, 0x7f, 0x92, 0x9a, 0x25, 0x44, 0xde, 0xee, 0xd5, 0xcc, 0x5f, 0x3e, 0x7c, 0x68, 0x8c, 0xc5, 0x97, 0x15, 0xac, 0xf7, 0xc4, 0x82, 0xf5, 0x3e, 0xa5, 0x60, 0x57, 0x0a, 0xc1, 0x7a, 0x52, 0x88,
0x99, 0xec, 0x01, 0x97, 0x19, 0x3d, 0x23, 0x25, 0x36, 0x8f, 0xa2, 0xd7, 0x20, 0xa4, 0x04, 0x82, 0x44, 0xf6, 0x69, 0xdb, 0x63, 0x71, 0x10, 0x9d, 0xc4, 0x07, 0xb3, 0x89, 0xfe, 0x38, 0x6b, 0x9e,
0xef, 0xc2, 0x33, 0x95, 0x07, 0x1e, 0x9d, 0x27, 0x8f, 0x9f, 0xe0, 0xd0, 0x8e, 0x3c, 0x34, 0x38, 0x10, 0x79, 0xbb, 0xd7, 0x30, 0x3f, 0xf9, 0xf0, 0xa1, 0x35, 0x11, 0x27, 0xb2, 0x07, 0x5c, 0x66,
0xd1, 0x73, 0x6d, 0xbd, 0x3d, 0x59, 0xe4, 0x35, 0x68, 0x44, 0x45, 0x8f, 0xbc, 0x65, 0x8d, 0xb5, 0xf4, 0x1b, 0x29, 0xb1, 0x79, 0x14, 0x83, 0x16, 0x21, 0x25, 0x10, 0x7c, 0x0f, 0x9e, 0xa9, 0xbc,
0x2b, 0xf8, 0x60, 0x44, 0x2d, 0x1b, 0xac, 0x49, 0x34, 0xd0, 0xc7, 0x2a, 0x28, 0x60, 0xb0, 0xf2, 0xf0, 0xe0, 0x34, 0x79, 0xfc, 0x04, 0x97, 0xf6, 0xe4, 0xa5, 0xc1, 0x91, 0x9e, 0x6b, 0xeb, 0xe3,
0x80, 0x87, 0x43, 0x9e, 0x1e, 0x5d, 0xc6, 0x03, 0x3d, 0x01, 0x3b, 0xb8, 0xa7, 0xa7, 0x2e, 0x07, 0xc9, 0x22, 0xaf, 0x41, 0x2b, 0x2a, 0x7a, 0xe4, 0x0d, 0x6b, 0xac, 0x5d, 0xc1, 0x07, 0x23, 0x6a,
0xf7, 0x30, 0x12, 0x4e, 0x42, 0xc1, 0x0f, 0x86, 0x17, 0x2a, 0x91, 0x6b, 0x10, 0xf7, 0x4c, 0x4e, 0xd9, 0x60, 0x4d, 0xa3, 0xa1, 0xbe, 0x56, 0x41, 0x01, 0x83, 0x95, 0x07, 0x3c, 0x1c, 0xf1, 0xf4,
0x4f, 0x05, 0xd7, 0xc9, 0x5b, 0x41, 0xc1, 0xcf, 0x3c, 0xe8, 0x22, 0x3f, 0x0f, 0x77, 0x1f, 0x1e, 0xe0, 0x3c, 0x1e, 0xea, 0x09, 0xd8, 0xde, 0x3d, 0x3d, 0x75, 0xd9, 0xbb, 0x87, 0x91, 0x70, 0x14,
0x4d, 0x4f, 0x0e, 0x85, 0x2e, 0x27, 0x3d, 0x5d, 0x4e, 0xfa, 0xaf, 0x40, 0x7b, 0xa0, 0x26, 0xb3, 0x0a, 0xbe, 0x37, 0x3a, 0x53, 0x89, 0x5c, 0x83, 0x78, 0x66, 0x72, 0x7c, 0x2c, 0xb8, 0x4e, 0xde,
0xaa, 0x9c, 0xaf, 0xf0, 0x4c, 0xec, 0x45, 0x34, 0x95, 0x7f, 0x17, 0x16, 0xc5, 0x65, 0x3c, 0x38, 0x0a, 0x0a, 0x7e, 0xee, 0x41, 0x1f, 0xf9, 0x79, 0xb8, 0xf5, 0xf0, 0x60, 0x76, 0xb4, 0x2f, 0x74,
0x14, 0x67, 0xce, 0x7c, 0xcc, 0xe6, 0xbe, 0xbf, 0xc0, 0x34, 0x5d, 0x51, 0xb3, 0x7e, 0x00, 0x2b, 0x39, 0xe9, 0xe9, 0x72, 0xd2, 0x7f, 0x05, 0xba, 0x43, 0x35, 0x99, 0x55, 0xe5, 0x7c, 0x85, 0x67,
0xf7, 0x47, 0x72, 0x58, 0xa1, 0x66, 0xfa, 0x9b, 0xd0, 0x8e, 0x84, 0x5c, 0x49, 0x5c, 0xb5, 0x59, 0x62, 0x2f, 0xa2, 0xa9, 0xfc, 0x3b, 0xb0, 0x28, 0xce, 0xe3, 0xe1, 0xbe, 0x38, 0x71, 0xe6, 0x63,
0x0e, 0xfb, 0x2f, 0x43, 0x6b, 0x24, 0xdf, 0xd4, 0xe6, 0x1c, 0xc4, 0x14, 0x51, 0xf0, 0x3c, 0x74, 0x36, 0xf7, 0xbb, 0x0b, 0x4c, 0xd3, 0x15, 0x35, 0xeb, 0xfb, 0xb0, 0x72, 0x7f, 0x2c, 0x87, 0x15,
0xf6, 0xf4, 0x77, 0x50, 0xf4, 0xc9, 0x0f, 0xf9, 0xa5, 0x52, 0x1e, 0x3e, 0xee, 0xbe, 0x01, 0x9d, 0x6a, 0xa6, 0xbf, 0x0e, 0xdd, 0x48, 0xc8, 0x9d, 0xc4, 0x55, 0x97, 0xe5, 0xb0, 0xff, 0x32, 0x74,
0xfc, 0xa7, 0x4d, 0xff, 0x36, 0xb4, 0x0e, 0x04, 0xee, 0xe0, 0x77, 0xf3, 0x2b, 0xe0, 0xd1, 0xbb, 0xc6, 0x72, 0xa5, 0x51, 0x73, 0x11, 0x53, 0x44, 0xc1, 0xf3, 0xd0, 0xdb, 0xd6, 0xff, 0x83, 0xa2,
0xd1, 0x68, 0x73, 0x4d, 0x81, 0x07, 0x62, 0x3f, 0x9c, 0x9e, 0x9d, 0x67, 0xef, 0x4f, 0x82, 0x85, 0x4f, 0x7e, 0xc0, 0xcf, 0x95, 0xf2, 0xf0, 0xe7, 0xd6, 0xeb, 0xd0, 0xcb, 0x3f, 0x16, 0xf5, 0x6f,
0x93, 0x16, 0xfd, 0xa9, 0xf9, 0xea, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x2a, 0x67, 0xa5, 0x42, 0x67, 0x4f, 0xe0, 0x09, 0x7e, 0x3f, 0x7f, 0x02, 0x1e, 0xbd, 0x13, 0x8d, 0xd7, 0xaf, 0x2a,
0xf6, 0x29, 0x00, 0x00, 0x70, 0x4f, 0xec, 0x84, 0xb3, 0x93, 0xd3, 0xec, 0xbd, 0x69, 0xb0, 0x70, 0xd4, 0xa1, 0x2f, 0x44,
0x5f, 0xfd, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0x10, 0x4a, 0x73, 0x6e, 0x2a, 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