bitxhub/internal/executor/contracts/appchain_manager.go

359 lines
11 KiB
Go

package contracts
import (
"encoding/json"
"fmt"
"strconv"
appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr"
"github.com/meshplus/bitxhub-core/boltvm"
"github.com/meshplus/bitxhub-model/constant"
"github.com/meshplus/bitxhub-model/pb"
)
type AppchainManager struct {
boltvm.Stub
appchainMgr.AppchainManager
}
type RegisterResult struct {
ChainMethod string `json:"chain_method"`
ProposalID string `json:"proposal_id"`
}
func (am *AppchainManager) Manager(des string, proposalResult string, extra []byte) *boltvm.Response {
specificAddrs := []string{constant.GovernanceContractAddr.Address().String()}
addrsData, err := json.Marshal(specificAddrs)
if err != nil {
return boltvm.Error("marshal specificAddrs error:" + string(err.Error()))
}
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSpecific)),
pb.String(""),
pb.String(am.CurrentCaller()),
pb.Bytes(addrsData))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
chain := &appchainMgr.Appchain{}
if err := json.Unmarshal(extra, chain); err != nil {
return boltvm.Error("unmarshal json error:" + err.Error())
}
ok, errData := am.AppchainManager.ChangeStatus(chain.ID, proposalResult)
if !ok {
return boltvm.Error(string(errData))
}
if proposalResult == string(APPOVED) {
switch des {
case appchainMgr.EventRegister:
return am.CrossInvoke(constant.InterchainContractAddr.String(), "Register", pb.String(chain.ID))
case appchainMgr.EventUpdate:
return responseWrapper(am.AppchainManager.UpdateAppchain(chain.ID, chain.Validators, chain.ConsensusType, chain.ChainType, chain.Name, chain.Desc, chain.Version, chain.PublicKey))
}
}
return boltvm.Success(nil)
}
// Apply: appchain managers apply appchain method and set appchainAdminDID for this new method
// @appchainAdminDID its address should be the same with am.Caller()
// @appchainMethod should be in format like did:bitxhub:appchain1:.
// @sig is the signature of appchain admin for appchainMethod
func (am *AppchainManager) Apply(appchainAdminDID, appchainMethod string, sig []byte) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
res := am.CrossInvoke(constant.MethodRegistryContractAddr.String(), "Apply",
pb.String(appchainAdminDID), pb.String(appchainMethod), pb.Bytes(sig))
if !res.Ok {
return res
}
res = am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventRegister),
pb.String(string(AppchainMgr)),
pb.Bytes([]byte(appchainMethod)),
)
if !res.Ok {
return res
}
res1 := RegisterResult{
ChainMethod: appchainMethod,
ProposalID: string(res.Result),
}
resData, err := json.Marshal(res1)
if err != nil {
return boltvm.Error(err.Error())
}
return boltvm.Success(resData)
}
// Register appchain managers registers appchain info caller is the appchain
// manager address return appchain id and error
func (am *AppchainManager) Register(appchainAdminDID, appchainMethod string, docAddr, docHash, validators string, consensusType, chainType, name, desc, version, pubkey string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
ok, idData := am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)
if ok {
return boltvm.Error("appchain has registered, chain id: " + string(idData))
}
ok, data := am.AppchainManager.GetAppchain(string(idData))
if !ok {
return boltvm.Error("get appchain error: " + string(data))
}
res := am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventRegister),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
if !res.Ok {
return res
}
res = am.CrossInvoke(constant.InterchainContractAddr.String(), "Register", pb.String(appchainMethod))
if !res.Ok {
return res
}
res = am.CrossInvoke(constant.MethodRegistryContractAddr.String(), "Register",
pb.String(appchainAdminDID), pb.String(appchainMethod),
pb.String(docAddr), pb.Bytes([]byte(docHash)), pb.Bytes(sig))
if !res.Ok {
return res
}
res1 := RegisterResult{
ChainMethod: am.Caller(),
ProposalID: string(res.Result),
}
resData, err := json.Marshal(res1)
if err != nil {
return boltvm.Error(err.Error())
}
return boltvm.Success(resData)
}
// UpdateAppchain updates available appchain
func (am *AppchainManager) UpdateAppchain(validators string, consensusType, chainType, name, desc, version, pubkey string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(am.Caller(), appchainMgr.EventUpdate); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: am.Caller(),
Name: name,
Validators: validators,
ConsensusType: consensusType,
Status: appchainMgr.AppchainUpdating,
ChainType: chainType,
Desc: desc,
Version: version,
PublicKey: pubkey,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventUpdate),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// UpdateAppchain updates approved appchain
func (am *AppchainManager) UpdateAppchain(appchainMethod, validators string, consensusType int32, chainType, name, desc, version, pubkey string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.UpdateAppchain(appchainMethod, validators, consensusType, chainType, name, desc, version, pubkey))
}
// FreezeAppchain freezes available appchain
func (am *AppchainManager) FreezeAppchain(id string) *boltvm.Response {
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSelfAdmin)),
pb.String(id),
pb.String(am.CurrentCaller()),
pb.Bytes(nil))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(id, appchainMgr.EventFreeze); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: id,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventFreeze),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// ActivateAppchain updates freezing appchain
func (am *AppchainManager) ActivateAppchain(id string) *boltvm.Response {
res := am.CrossInvoke(constant.RoleContractAddr.String(), "CheckPermission",
pb.String(string(PermissionSelfAdmin)),
pb.String(id),
pb.String(am.CurrentCaller()),
pb.Bytes(nil))
if !res.Ok {
return boltvm.Error("check permission error:" + string(res.Result))
}
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(id, appchainMgr.EventActivate); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: id,
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventActivate),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// AuditApply bitxhub manager audit appchain method apply
func (am *AppchainManager) AuditApply(relayAdminDID, proposerMethod string, isApproved int32, sig []byte) *boltvm.Response {
if res := am.IsAdmin(); !res.Ok {
return res
}
res := am.CrossInvoke(constant.MethodRegistryContractAddr.String(), "AuditApply",
pb.String(relayAdminDID), pb.String(proposerMethod), pb.Int32(isApproved), pb.Bytes(sig))
return responseWrapper(res.Ok, res.Result)
}
// LogoutAppchain updates available appchain
func (am *AppchainManager) LogoutAppchain() *boltvm.Response {
am.AppchainManager.Persister = am.Stub
if ok, data := am.AppchainManager.ChangeStatus(am.Caller(), appchainMgr.EventLogout); !ok {
return boltvm.Error(string(data))
}
chain := &appchainMgr.Appchain{
ID: am.Caller(),
}
data, err := json.Marshal(chain)
if err != nil {
return boltvm.Error(err.Error())
}
return am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal",
pb.String(am.Caller()),
pb.String(appchainMgr.EventLogout),
pb.String(string(AppchainMgr)),
pb.Bytes(data),
)
}
// CountApprovedAppchains counts all approved appchains
func (am *AppchainManager) CountAvailableAppchains() *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.CountAvailableAppchains())
}
// CountAppchains counts all appchains including approved, rejected or registered
func (am *AppchainManager) CountAppchains() *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.CountAppchains())
}
// Appchains returns all appchains
func (am *AppchainManager) Appchains() *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.Appchains())
}
// GetAppchain returns appchain info by appchain id
func (am *AppchainManager) GetAppchain(id string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.GetAppchain(id))
}
// GetPubKeyByChainID can get aim chain's public key using aim chain ID
func (am *AppchainManager) GetPubKeyByChainID(id string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
return responseWrapper(am.AppchainManager.GetPubKeyByChainID(id))
}
func (am *AppchainManager) DeleteAppchain(relayAdminDID, toDeleteMethod string, sig []byte) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
if res := am.IsAdmin(); !res.Ok {
return res
}
res := am.CrossInvoke(constant.InterchainContractAddr.String(), "DeleteInterchain", pb.String(toDeleteMethod))
if !res.Ok {
return res
}
res = am.CrossInvoke(constant.MethodRegistryContractAddr.String(), "Delete", pb.String(relayAdminDID), pb.String(toDeleteMethod), pb.Bytes(sig))
if !res.Ok {
return res
}
return responseWrapper(am.AppchainManager.DeleteAppchain(toDeleteMethod))
}
func (am *AppchainManager) IsAdmin() *boltvm.Response {
ret := am.CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", pb.String(am.Caller()))
is, err := strconv.ParseBool(string(ret.Result))
if err != nil {
return boltvm.Error(fmt.Errorf("judge caller type: %w", err).Error())
}
if !is {
return boltvm.Error("caller is not an admin account")
}
return boltvm.Success([]byte("1"))
}
func responseWrapper(ok bool, data []byte) *boltvm.Response {
if ok {
return boltvm.Success(data)
}
return boltvm.Error(string(data))
}
func (am *AppchainManager) IsAvailable(chainId string) *boltvm.Response {
am.AppchainManager.Persister = am.Stub
is, data := am.AppchainManager.GetAppchain(chainId)
if !is {
return boltvm.Error("get appchain info error: " + string(data))
}
app := &appchainMgr.Appchain{}
if err := json.Unmarshal(data, app); err != nil {
return boltvm.Error("unmarshal error: " + err.Error())
}
if app.Status != appchainMgr.AppchainAvailable {
return boltvm.Error("the appchain status is " + string(app.Status))
}
return boltvm.Success(nil)
}