diff --git a/genesis/gen_test.go b/genesis/gen_test.go index 18ff69a77..403da98ed 100644 --- a/genesis/gen_test.go +++ b/genesis/gen_test.go @@ -1,3 +1,18 @@ +// Copyright 2015-2017 Monax Industries Limited. +// This file is part of the Monax platform (Monax) + +// Monax is free software: you can use, redistribute it and/or modify +// it only under the terms of the GNU General Public License, version +// 3, as published by the Free Software Foundation. + +// Monax is distributed WITHOUT ANY WARRANTY pursuant to +// the terms of the Gnu General Public Licence, version 3, including +// (but not limited to) Clause 15 thereof. See the text of the +// GNU General Public License, version 3 for full terms. + +// You should have received a copy of the GNU General Public License, +// version 3, with Monax. If not, see . + package genesis import ( diff --git a/genesis/genesis.go b/genesis/genesis.go new file mode 100644 index 000000000..fb348693f --- /dev/null +++ b/genesis/genesis.go @@ -0,0 +1,85 @@ +// Copyright 2015-2017 Monax Industries Limited. +// This file is part of the Monax platform (Monax) + +// Monax is free software: you can use, redistribute it and/or modify +// it only under the terms of the GNU General Public License, version +// 3, as published by the Free Software Foundation. + +// Monax is distributed WITHOUT ANY WARRANTY pursuant to +// the terms of the Gnu General Public Licence, version 3, including +// (but not limited to) Clause 15 thereof. See the text of the +// GNU General Public License, version 3 for full terms. + +// You should have received a copy of the GNU General Public License, +// version 3, with Monax. If not, see . + +package genesis + +import ( + "bytes" + "encoding/json" + "time" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + wire "github.com/tendermint/go-wire" +) + +// MakeGenesisDocFromAccounts takes a chainName and a slice of pointers to GenesisAccount, +// and a slice of pointers to GenesisValidator to construct a GenesisDoc, or returns an error on +// failure. In particular MakeGenesisDocFromAccount uses the local time as a +// timestamp for the GenesisDoc. +func MakeGenesisDocFromAccounts(chainName string, accounts []*GenesisAccount, + validators []*GenesisValidator) (GenesisDoc, error) { + + // TODO: assert valid accounts and validators + // TODO: [ben] expose setting global permissions + globalPermissions := ptypes.DefaultAccountPermissions.Clone() + genesisParameters := &GenesisParams{ + GlobalPermissions: &globalPermissions, + } + // copy slice of pointers to accounts into slice of accounts + accountsCopy := make([]GenesisAccount, len(accounts)) + for i, genesisAccount := range accounts { + accountsCopy[i] = genesisAccount.Clone() + } + // copy slice of pointers to validators into slice of validators + validatorsCopy := make([]GenesisValidator, len(validators)) + for i, genesisValidator := range validators { + genesisValidatorCopy, err := genesisValidator.Clone() + if err != nil { + return GenesisDoc{}, err + } + validatorsCopy[i] = genesisValidatorCopy + } + genesisDoc := GenesisDoc{ + GenesisTime: time.Now(), + // TODO: this needs to be corrected for ChainName, and ChainId + // is the derived hash from the GenesisDoc serialised bytes + ChainID: chainName, + Params: genesisParameters, + Accounts: accountsCopy, + Validators: validatorsCopy, + } + return genesisDoc, nil +} + +// GetGenesisFileBytes returns the JSON (not-yet) canonical bytes for a given +// GenesisDoc or an error. In a first rewrite, rely on go-wire +// for the JSON serialisation with type-bytes. +func GetGenesisFileBytes(genesisDoc *GenesisDoc) ([]byte, error) { + + // TODO: write JSON in canonical order + var err error + buffer, n := new(bytes.Buffer), new(int) + // write JSON with go-wire type-bytes (for public keys) + wire.WriteJSON(genesisDoc, buffer, n, &err) + if err != nil { + return nil, err + } + // rewrite buffer with indentation + indentedBuffer := new(bytes.Buffer) + if err := json.Indent(indentedBuffer, buffer.Bytes(), "", "\t"); err != nil { + return nil, err + } + return indentedBuffer.Bytes(), nil +} diff --git a/genesis/make_genesis_file.go b/genesis/make_genesis_file.go index f67a86d87..4fffd203d 100644 --- a/genesis/make_genesis_file.go +++ b/genesis/make_genesis_file.go @@ -27,6 +27,25 @@ func GenerateKnown(chainID, accountsPathCSV, validatorsPathCSV string) (string, time.Now()) } +//------------------------------------------------------------------------------------ +// interface functions that are consumed by monax tooling + +func GenerateGenesisFileBytes(chainName string, genesisAccounts []*GenesisAccount, + genesisValidators []*GenesisValidator) ([]byte, error) { + genesisDoc, err := MakeGenesisDocFromAccounts(chainName, genesisAccounts, genesisValidators) + + buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int) + wire.WriteJSON(genesisDoc, buf, n, &err) + if err != nil { + return nil, err + } + if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil { + return nil, err + } + + return buf2.Bytes(), nil +} + //------------------------------------------------------------------------------------ // core functions that provide functionality for monax tooling in v0.16 diff --git a/genesis/maker.go b/genesis/maker.go new file mode 100644 index 000000000..b9a403726 --- /dev/null +++ b/genesis/maker.go @@ -0,0 +1,85 @@ +// Copyright 2015-2017 Monax Industries Limited. +// This file is part of the Monax platform (Monax) + +// Monax is free software: you can use, redistribute it and/or modify +// it only under the terms of the GNU General Public License, version +// 3, as published by the Free Software Foundation. + +// Monax is distributed WITHOUT ANY WARRANTY pursuant to +// the terms of the Gnu General Public Licence, version 3, including +// (but not limited to) Clause 15 thereof. See the text of the +// GNU General Public License, version 3 for full terms. + +// You should have received a copy of the GNU General Public License, +// version 3, with Monax. If not, see . + +package genesis + +import ( + "fmt" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + + "github.com/tendermint/go-crypto" +) + +const ( + PublicKeyEd25519ByteLength int = 32 + PublicKeySecp256k1ByteLength int = 64 +) + +// NewGenesisAccount returns a new GenesisAccount +func NewGenesisAccount(address []byte, amount int64, name string, + permissions *ptypes.AccountPermissions) *GenesisAccount { + return &GenesisAccount{ + Address: address, + Amount: amount, + Name: name, + Permissions: permissions, + } +} + +func NewGenesisValidator(amount int64, name string, unbondToAddress []byte, + unbondAmount int64, keyType string, publicKeyBytes []byte) (*GenesisValidator, error) { + // convert the key bytes into a typed fixed size byte array + var typedPublicKeyBytes []byte + switch keyType { + case "ed25519": + // TODO: [ben] functionality and checks need to be inherit in the type + if len(publicKeyBytes) != PublicKeyEd25519ByteLength { + return nil, fmt.Errorf("Invalid length provided for ed25519 public key (len %v)", + len(publicKeyBytes)) + } + // ed25519 has type byte 0x01 + typedPublicKeyBytes = make([]byte, PublicKeyEd25519ByteLength+1) + // prepend type byte to public key + typedPublicKeyBytes = append([]byte{crypto.PubKeyTypeEd25519}, publicKeyBytes...) + case "secp256k1": + if len(publicKeyBytes) != PublicKeySecp256k1ByteLength { + return nil, fmt.Errorf("Invalid length provided for secp256k1 public key (len %v)", + len(publicKeyBytes)) + } + // secp256k1 has type byte 0x02 + typedPublicKeyBytes = make([]byte, PublicKeySecp256k1ByteLength+1) + // prepend type byte to public key + typedPublicKeyBytes = append([]byte{crypto.PubKeyTypeSecp256k1}, publicKeyBytes...) + default: + return nil, fmt.Errorf("Unsupported key type (%s)", keyType) + } + newPublicKey, err := crypto.PubKeyFromBytes(typedPublicKeyBytes) + if err != nil { + return nil, err + } + // ability to unbond to multiple accounts currently unused + var unbondTo []BasicAccount + + return &GenesisValidator{ + PubKey: newPublicKey, + Amount: unbondAmount, + Name: name, + UnbondTo: append(unbondTo, BasicAccount{ + Address: unbondToAddress, + Amount: unbondAmount, + }), + }, nil +} diff --git a/genesis/types.go b/genesis/types.go index d03e61976..40395fb79 100644 --- a/genesis/types.go +++ b/genesis/types.go @@ -1,3 +1,18 @@ +// Copyright 2015-2017 Monax Industries Limited. +// This file is part of the Monax platform (Monax) + +// Monax is free software: you can use, redistribute it and/or modify +// it only under the terms of the GNU General Public License, version +// 3, as published by the Free Software Foundation. + +// Monax is distributed WITHOUT ANY WARRANTY pursuant to +// the terms of the Gnu General Public Licence, version 3, including +// (but not limited to) Clause 15 thereof. See the text of the +// GNU General Public License, version 3 for full terms. + +// You should have received a copy of the GNU General Public License, +// version 3, with Monax. If not, see . + package genesis import ( @@ -6,6 +21,7 @@ import ( "time" ptypes "github.com/eris-ltd/eris-db/permission/types" + "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -37,6 +53,19 @@ type GenesisValidator struct { UnbondTo []BasicAccount `json:"unbond_to"` } +// GenesisPrivateValidator marshals the state of the private +// validator for the purpose of Genesis creation; and hence +// is defined in genesis and not under consensus, where +// PrivateValidator (currently inherited from Tendermint) is. +type GenesisPrivateValidator struct { + Address string `json:"address"` + PubKey []interface{} `json:"pub_key"` + PrivKey []interface{} `json:"priv_key"` + LastHeight int64 `json:"last_height"` + LastRound int64 `json:"last_round"` + LastStep int64 `json:"last_step"` +} + type GenesisParams struct { GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"` } @@ -65,3 +94,65 @@ func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { } return } + +//------------------------------------------------------------ +// Methods for genesis types +// NOTE: breaks formatting convention +// TODO: split each genesis type in its own file definition + +//------------------------------------------------------------ +// GenesisAccount methods + +// Clone clones the genesis account +func (genesisAccount *GenesisAccount) Clone() GenesisAccount { + // clone the address + addressClone := make([]byte, len(genesisAccount.Address)) + copy(addressClone, genesisAccount.Address) + // clone the account permissions + accountPermissionsClone := genesisAccount.Permissions.Clone() + return GenesisAccount{ + Address: addressClone, + Amount: genesisAccount.Amount, + Name: genesisAccount.Name, + Permissions: &accountPermissionsClone, + } +} + +//------------------------------------------------------------ +// GenesisValidator methods + +// Clone clones the genesis validator +func (genesisValidator *GenesisValidator) Clone() (GenesisValidator, error) { + if genesisValidator == nil { + return GenesisValidator{}, fmt.Errorf("Cannot clone nil GenesisValidator.") + } + if genesisValidator.PubKey == nil { + return GenesisValidator{}, fmt.Errorf("Invalid GenesisValidator %s with nil public key.", + genesisValidator.Name) + } + // clone the addresses to unbond to + unbondToClone := make([]BasicAccount, len(genesisValidator.UnbondTo)) + for i, basicAccount := range genesisValidator.UnbondTo { + unbondToClone[i] = basicAccount.Clone() + } + return GenesisValidator{ + PubKey: genesisValidator.PubKey, + Amount: genesisValidator.Amount, + Name: genesisValidator.Name, + UnbondTo: unbondToClone, + }, nil +} + +//------------------------------------------------------------ +// BasicAccount methods + +// Clone clones the basic account +func (basicAccount *BasicAccount) Clone() BasicAccount { + // clone the address + addressClone := make([]byte, len(basicAccount.Address)) + copy(addressClone, basicAccount.Address) + return BasicAccount{ + Address: addressClone, + Amount: basicAccount.Amount, + } +} diff --git a/manager/eris-mint/state/genesis_test.go b/manager/eris-mint/state/genesis_test.go index c9305ed4b..47541d931 100644 --- a/manager/eris-mint/state/genesis_test.go +++ b/manager/eris-mint/state/genesis_test.go @@ -31,7 +31,7 @@ var g1 = fmt.Sprintf(` { "address": "%X", "amount": %d, - "name": "%s", + "name": "%s", "permissions": { "base": { "perms": %d, diff --git a/manager/eris-mint/state/types/genesis.go b/manager/eris-mint/state/types/genesis.go deleted file mode 100644 index 90d54cf72..000000000 --- a/manager/eris-mint/state/types/genesis.go +++ /dev/null @@ -1,63 +0,0 @@ -package types - -import ( - "fmt" - "time" - - "github.com/eris-ltd/eris-db/common/sanity" - ptypes "github.com/eris-ltd/eris-db/permission/types" - - "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" -) - -//------------------------------------------------------------ -// we store the gendoc in the db - -var GenDocKey = []byte("GenDocKey") - -//------------------------------------------------------------ -// core types for a genesis definition - -type BasicAccount struct { - Address []byte `json:"address"` - Amount int64 `json:"amount"` -} - -type GenesisAccount struct { - Address []byte `json:"address"` - Amount int64 `json:"amount"` - Name string `json:"name"` - Permissions *ptypes.AccountPermissions `json:"permissions"` -} - -type GenesisValidator struct { - PubKey crypto.PubKey `json:"pub_key"` - Amount int64 `json:"amount"` - Name string `json:"name"` - UnbondTo []BasicAccount `json:"unbond_to"` -} - -type GenesisParams struct { - GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"` -} - -type GenesisDoc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - Params *GenesisParams `json:"params"` - Accounts []GenesisAccount `json:"accounts"` - Validators []GenesisValidator `json:"validators"` -} - -//------------------------------------------------------------ -// Make genesis state from file - -func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { - var err error - wire.ReadJSONPtr(&genState, jsonBlob, &err) - if err != nil { - sanity.PanicCrisis(fmt.Sprintf("Couldn't read GenesisDoc: %v", err)) - } - return -} diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 03ff4b6ba..c6ebd5847 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -168,6 +168,21 @@ func (aP *AccountPermissions) RmRole(role string) bool { return false } +// Clone clones the account permissions +func (accountPermissions *AccountPermissions) Clone() AccountPermissions { + // clone base permissions + basePermissionsClone := accountPermissions.Base + // clone roles []string + rolesClone := make([]string, len(accountPermissions.Roles)) + // strings are immutable so copy suffices + copy(rolesClone, accountPermissions.Roles) + + return AccountPermissions{ + Base: basePermissionsClone, + Roles: rolesClone, + } +} + //-------------------------------------------------------------------------------- // string utilities diff --git a/permission/types/util.go b/permission/types/util.go new file mode 100644 index 000000000..15a377ef9 --- /dev/null +++ b/permission/types/util.go @@ -0,0 +1,35 @@ +package types + +// ConvertMapStringIntToPermissions converts a map of string-integer pairs and a slice of +// strings for the roles to an AccountPermissions type. The integer needs to be greater +// than zero to set the permission. For all unmentioned permissions the ZeroBasePermissions +// is defaulted to. +// TODO: [ben] re-evaluate the use of int for setting the permission. +func ConvertPermissionsMapAndRolesToAccountPermissions(permissions map[string]int, roles []string) (*AccountPermissions, error) { + var err error + accountPermissions := &AccountPermissions{} + accountPermissions.Base, err = convertPermissionsMapStringIntToBasePermissions(permissions) + if err != nil { + return nil, err + } + accountPermissions.Roles = roles + return accountPermissions, nil +} + +// convertPermissionsMapStringIntToBasePermissions converts a map of string-integer pairs to +// BasePermissions. +func convertPermissionsMapStringIntToBasePermissions(permissions map[string]int) (BasePermissions, error) { + // initialise basePermissions as ZeroBasePermissions + basePermissions := ZeroBasePermissions + + for permissionName, value := range permissions { + permissionsFlag, err := PermStringToFlag(permissionName) + if err != nil { + return basePermissions, err + } + // sets the permissions bitflag and the setbit flag for the permission. + basePermissions.Set(permissionsFlag, value > 0) + } + + return basePermissions, nil +} diff --git a/rpc/tendermint/test/shared.go b/rpc/tendermint/test/shared.go index a3d0310cc..0d75ea279 100644 --- a/rpc/tendermint/test/shared.go +++ b/rpc/tendermint/test/shared.go @@ -19,7 +19,7 @@ import ( "github.com/eris-ltd/eris-db/txs" "github.com/eris-ltd/eris-db/word256" - state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + genesis "github.com/eris-ltd/eris-db/genesis" "github.com/spf13/viper" "github.com/tendermint/go-crypto" rpcclient "github.com/tendermint/go-rpc/client" @@ -33,7 +33,7 @@ var ( mempoolCount = 0 chainID string websocketAddr string - genesisDoc *state_types.GenesisDoc + genesisDoc *genesis.GenesisDoc websocketEndpoint string user = makeUsers(5) // make keys jsonRpcClient rpcclient.Client @@ -48,7 +48,7 @@ func initGlobalVariables(ffs *fixtures.FileFixtures) error { rootWorkDir = ffs.AddDir("rootWorkDir") rootDataDir := ffs.AddDir("rootDataDir") genesisFile := ffs.AddFile("rootWorkDir/genesis.json", defaultGenesis) - genesisDoc = state_types.GenesisDocFromJSON([]byte(defaultGenesis)) + genesisDoc = genesis.GenesisDocFromJSON([]byte(defaultGenesis)) if ffs.Error != nil { return ffs.Error diff --git a/test/testdata/testdata/testdata.go b/test/testdata/testdata/testdata.go index 5fd2be000..11120bb90 100644 --- a/test/testdata/testdata/testdata.go +++ b/test/testdata/testdata/testdata.go @@ -5,7 +5,7 @@ import ( consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" event "github.com/eris-ltd/eris-db/event" - stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + genesis "github.com/eris-ltd/eris-db/genesis" rpc_v0 "github.com/eris-ltd/eris-db/rpc/v0" "github.com/eris-ltd/eris-db/rpc/v0/shared" transaction "github.com/eris-ltd/eris-db/txs" @@ -540,7 +540,7 @@ var serverDuration uint = 100 type ( ChainData struct { PrivValidator *mintTypes.PrivValidator `json:"priv_validator"` - Genesis *stypes.GenesisDoc `json:"genesis"` + Genesis *genesis.GenesisDoc `json:"genesis"` } GetAccountData struct {