contract/enterprise/execute.go
package enterprise
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/aergoio/aergo-lib/log"
"github.com/aergoio/aergo/v2/consensus"
"github.com/aergoio/aergo/v2/state"
"github.com/aergoio/aergo/v2/state/statedb"
"github.com/aergoio/aergo/v2/types"
)
var (
entLogger *log.Logger
ErrNotSupportedMethod = errors.New("Not supported Enterprise Tx")
ErrTxEnterpriseAlreadyIncludeChangeCluster = errors.New("Enterprise Tx of Change cluster type already included in the block")
)
type EnterpriseContext struct {
Call *types.CallInfo
Args []string
ArgsAny []interface{}
Admins [][]byte
Conf *Conf
}
func init() {
entLogger = log.NewLogger("enterprise")
}
func (e *EnterpriseContext) IsAdminExist(addr []byte) bool {
for _, a := range e.Admins {
if bytes.Equal(a, addr) {
return true
}
}
return false
}
func (e *EnterpriseContext) HasConfValue(value string) bool {
if e.Conf != nil {
for _, v := range e.Conf.Values {
if v == value {
return true
}
}
}
return false
}
func ExecuteEnterpriseTx(bs *state.BlockState, ccc consensus.ChainConsensusCluster, scs *statedb.ContractState, txBody *types.TxBody,
sender, receiver *state.AccountState, blockNo types.BlockNo) ([]*types.Event, error) {
context, err := ValidateEnterpriseTx(txBody, sender, scs, blockNo)
if err != nil {
return nil, err
}
var events []*types.Event
switch context.Call.Name {
case AppendAdmin:
requestAddress := types.ToAddress(context.Args[0])
err := setAdmins(scs,
append(context.Admins, requestAddress))
if err != nil {
return nil, err
}
jsonArgs, err := json.Marshal(context.Args[0])
if err != nil {
return nil, err
}
events = append(events, &types.Event{
ContractAddress: receiver.ID(),
EventName: "Append ADMIN",
EventIdx: 0,
JsonArgs: string(jsonArgs),
})
case RemoveAdmin:
for i, v := range context.Admins {
if bytes.Equal(v, types.ToAddress(context.Args[0])) {
context.Admins = append(context.Admins[:i], context.Admins[i+1:]...)
break
}
}
err := setAdmins(scs, context.Admins)
if err != nil {
return nil, err
}
jsonArgs, err := json.Marshal(context.Args[0])
if err != nil {
return nil, err
}
events = append(events, &types.Event{
ContractAddress: receiver.ID(),
EventName: "Remove ADMIN",
EventIdx: 0,
JsonArgs: string(jsonArgs),
})
case SetConf, AppendConf, RemoveConf:
key := context.Args[0]
err = setConf(scs, []byte(key), context.Conf)
if err != nil {
return nil, err
}
events, err = createSetEvent(receiver.ID(), key, context.Conf.Values)
if err != nil {
return nil, err
}
case EnableConf:
key := context.Args[0]
err = setConf(scs, []byte(key), context.Conf)
if err != nil {
return nil, err
}
jsonArgs, err := json.Marshal(context.Call.Args[1])
if err != nil {
return nil, err
}
events = append(events, &types.Event{
ContractAddress: receiver.ID(),
EventName: "Enable " + strings.ToUpper(string(key)),
EventIdx: 0,
JsonArgs: string(jsonArgs),
})
case ChangeCluster:
if bs.CCProposal != nil {
return nil, ErrTxEnterpriseAlreadyIncludeChangeCluster
}
ccReq, ok := context.ArgsAny[0].(*types.MembershipChange)
if !ok {
return nil, fmt.Errorf("invalid argument of cluster change request")
}
var (
ccChange *consensus.ConfChangePropose
err error
)
// MakeConfChangeProposal can make different results depending on the raft status. therefore the result shouldn not be written on receipt
if ccChange, err = ccc.MakeConfChangeProposal(ccReq); err != nil {
if err != consensus.ErrorMembershipChangeSkip {
entLogger.Error().Str("cluster change", ccReq.ToString()).Err(err).Msg("Enterprise tx: failed to make cluster change proposal")
} else {
entLogger.Info().Str("cluster change", ccReq.ToString()).Msg("Enterprise tx: skipped since this node is not leader")
}
} else {
bs.CCProposal = ccChange
}
/*
jsonArgs, err := json.Marshal(context.Call.Args[0])
if err != nil {
return nil, err
}
entLogger.Debug().Str("jsonarg", string(jsonArgs)).Msg("make event")
events = append(events, &types.Event{
ContractAddress: []byte(types.AergoEnterprise),
EventName: "ChangeCluster ",
EventIdx: 0,
JsonArgs: string(jsonArgs),
})
*/
default:
return nil, fmt.Errorf("unsupported call in enterprise contract")
}
return events, nil
}
func createSetEvent(addr []byte, name string, v []string) ([]*types.Event, error) {
jsonArgs, err := json.Marshal(v)
if err != nil {
return nil, err
}
return []*types.Event{
{
ContractAddress: addr,
EventName: "Set " + strings.ToUpper(name),
EventIdx: 0,
JsonArgs: string(jsonArgs),
},
}, nil
}