aergoio/aergo

View on GitHub
contract/enterprise/execute.go

Summary

Maintainability
B
6 hrs
Test Coverage
C
76%
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
}