aergoio/aergo

View on GitHub
contract/enterprise/config.go

Summary

Maintainability
A
35 mins
Test Coverage
C
72%
package enterprise

import (
    "fmt"
    "strings"

    "github.com/aergoio/aergo/v2/state/statedb"
    "github.com/aergoio/aergo/v2/types"
    "github.com/aergoio/aergo/v2/types/dbkey"
)

const (
    RPCPermissions = "RPCPERMISSIONS"
    P2PWhite       = "P2PWHITE"
    P2PBlack       = "P2PBLACK"
    AccountWhite   = "ACCOUNTWHITE"
)

// EnterpriseKeyDict is represent allowed key list and used when validate tx, int values are meaningless.
var enterpriseKeyDict = map[string]int{
    RPCPermissions: 1,
    P2PWhite:       2,
    P2PBlack:       3,
    AccountWhite:   4,
}

type Conf struct {
    On     bool
    Values []string
}

func (c *Conf) AppendValue(r string) {
    if c.Values == nil {
        c.Values = []string{}
    }
    c.Values = append(c.Values, r)
}
func (c *Conf) RemoveValue(r string) {
    for i, v := range c.Values {
        if v == r {
            c.Values = append(c.Values[:i], c.Values[i+1:]...)
            break
        }
    }
}

func (c *Conf) Validate(key []byte, context *EnterpriseContext) error {
    if !c.On {
        return nil
    }
    strKey := strings.ToUpper(string(key))
    switch strKey {
    case RPCPermissions:
        for _, v := range c.Values {
            if strings.Contains(strings.ToUpper(strings.Split(v, ":")[1]), "W") {
                return nil
            }
        }
        return fmt.Errorf("the values of %s should have at least one write permission", strKey)
    case AccountWhite:
        for _, v := range context.Admins {
            address := types.EncodeAddress(v)
            if context.HasConfValue(address) {
                return nil
            }
        }
        return fmt.Errorf("the values of %s should have at least one admin address", strKey)
    default:
        return nil
    }
}

func GetConf(ecs *statedb.ContractState, key string) (*types.EnterpriseConfig, error) {
    ret := &types.EnterpriseConfig{Key: key}
    if strings.ToUpper(key) == "PERMISSIONS" {
        for k := range enterpriseKeyDict {
            ret.Values = append(ret.Values, k)
        }
    } else {
        conf, err := getConf(ecs, []byte(key))
        if err != nil {
            return nil, err
        }
        if conf != nil {
            ret.On = conf.On
            ret.Values = conf.Values
        }
    }
    return ret, nil
}

func enableConf(scs *statedb.ContractState, key []byte, value bool) (*Conf, error) {
    conf, err := getConf(scs, key)
    if err != nil {
        return nil, err
    }
    if conf != nil {
        conf.On = value
    } else {
        conf = &Conf{On: value}
    }
    return conf, nil
}

func getConf(scs *statedb.ContractState, key []byte) (*Conf, error) {
    data, err := scs.GetData(dbkey.EnterpriseConf(key))
    if err != nil || data == nil {
        return nil, err
    }
    return deserializeConf(data), err
}

func setConfValues(scs *statedb.ContractState, key []byte, values []string) (*Conf, error) {
    conf, err := getConf(scs, key)
    if err != nil {
        return nil, err
    }
    if conf != nil {
        conf.Values = values
    } else {
        conf = &Conf{Values: values}
    }
    return conf, nil
}

func setConf(scs *statedb.ContractState, key []byte, conf *Conf) error {
    return scs.SetData(dbkey.EnterpriseConf(key), serializeConf(conf))
}

func serializeConf(c *Conf) []byte {
    var ret []byte
    if c.On {
        ret = append(ret, 1)
    } else {
        ret = append(ret, 0)
    }
    for _, v := range c.Values {
        ret = append(ret, '\\')
        ret = append(ret, []byte(v)...)
    }
    return ret
}

func deserializeConf(data []byte) *Conf {
    ret := &Conf{
        On:     false,
        Values: strings.Split(string(data), "\\")[1:],
    }
    if data[0] == 1 {
        ret.On = true
    }
    return ret
}