Browse Source

Refactoring genesis, including PubKey into TxInput

pull/9/head
Jae Kwon 10 years ago
parent
commit
f91665fe07
17 changed files with 289 additions and 159 deletions
  1. +0
    -10
      account/account.go
  2. +1
    -1
      account/privkey.go
  3. +12
    -17
      account/pubkey.go
  4. +1
    -1
      account/signature.go
  5. +2
    -2
      binary/int.go
  6. +4
    -0
      block/tx.go
  7. +2
    -2
      cmd/daemon.go
  8. +10
    -10
      cmd/gen_validator.go
  9. +1
    -3
      common/cmap.go
  10. +6
    -0
      common/panic.go
  11. +84
    -83
      config/config.go
  12. +37
    -0
      db/db.go
  13. +7
    -2
      db/level_db.go
  14. +64
    -14
      state/genesis.go
  15. +10
    -5
      state/priv_validator.go
  16. +23
    -1
      state/state.go
  17. +25
    -8
      state/test.go

+ 0
- 10
account/account.go View File

@ -36,16 +36,6 @@ type Account struct {
Balance uint64
}
func NewAccount(pubKey PubKey) *Account {
address := pubKey.Address()
return &Account{
Address: address,
PubKey: pubKey,
Sequence: uint(0),
Balance: uint64(0),
}
}
func (account *Account) Copy() *Account {
accountCopy := *account
return &accountCopy


+ 1
- 1
account/privkey.go View File

@ -26,7 +26,7 @@ const (
func PrivKeyDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case PrivKeyTypeEd25519:
return ReadBinary(&PrivKeyEd25519{}, r, n, err)
return ReadBinary(PrivKeyEd25519{}, r, n, err)
default:
*err = Errorf("Unknown PrivKey type %X", t)
return nil


+ 12
- 17
account/pubkey.go View File

@ -18,8 +18,9 @@ type PubKey interface {
// Types of PubKey implementations
const (
PubKeyTypeUnknown = byte(0x00) // For pay-to-pubkey-hash txs.
PubKeyTypeEd25519 = byte(0x01)
PubKeyTypeNil = byte(0x00)
PubKeyTypeUnknown = byte(0x01) // For pay-to-pubkey-hash txs.
PubKeyTypeEd25519 = byte(0x02)
)
//-------------------------------------
@ -27,10 +28,10 @@ const (
func PubKeyDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case PubKeyTypeUnknown:
return PubKeyUnknown{}
case PubKeyTypeNil:
return PubKeyNil{}
case PubKeyTypeEd25519:
return ReadBinary(&PubKeyEd25519{}, r, n, err)
return ReadBinary(PubKeyEd25519{}, r, n, err)
default:
*err = Errorf("Unknown PubKey type %X", t)
return nil
@ -45,22 +46,16 @@ var _ = RegisterType(&TypeInfo{
//-------------------------------------
// Implements PubKey
// For pay-to-pubkey-hash txs, where the TxOutput PubKey
// is not known in advance, only its hash (address).
type PubKeyUnknown struct {
address []byte
}
func NewPubKeyUnknown(address []byte) PubKeyUnknown { return PubKeyUnknown{address} }
type PubKeyNil struct{}
func (key PubKeyUnknown) TypeByte() byte { return PubKeyTypeUnknown }
func (key PubKeyNil) TypeByte() byte { return PubKeyTypeNil }
func (key PubKeyUnknown) Address() []byte {
return key.address
func (key PubKeyNil) Address() []byte {
panic("PubKeyNil has no address")
}
func (key PubKeyUnknown) VerifyBytes(msg []byte, sig_ Signature) bool {
panic("PubKeyUnknown cannot verify messages")
func (key PubKeyNil) VerifyBytes(msg []byte, sig_ Signature) bool {
panic("PubKeyNil cannot verify messages")
}
//-------------------------------------


+ 1
- 1
account/signature.go View File

@ -25,7 +25,7 @@ const (
func SignatureDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case SignatureTypeEd25519:
return ReadBinary(&SignatureEd25519{}, r, n, err)
return ReadBinary(SignatureEd25519{}, r, n, err)
default:
*err = Errorf("Unknown Signature type %X", t)
return nil


+ 2
- 2
binary/int.go View File

@ -159,7 +159,7 @@ func ReadUint64(r io.Reader, n *int64, err *error) uint64 {
// Varint
func WriteVarint(i int, w io.Writer, n *int64, err *error) {
buf := make([]byte, 9)
buf := make([]byte, binary.MaxVarintLen64)
n_ := int64(binary.PutVarint(buf, int64(i)))
*n += n_
WriteTo(buf[:n_], w, n, err)
@ -175,7 +175,7 @@ func ReadVarint(r io.Reader, n *int64, err *error) int {
// Uvarint
func WriteUvarint(i uint, w io.Writer, n *int64, err *error) {
buf := make([]byte, 9)
buf := make([]byte, binary.MaxVarintLen64)
n_ := int64(binary.PutUvarint(buf, uint64(i)))
*n += n_
WriteTo(buf[:n_], w, n, err)


+ 4
- 0
block/tx.go View File

@ -15,6 +15,9 @@ var (
ErrTxDuplicateAddress = errors.New("Error duplicate address")
ErrTxInvalidAmount = errors.New("Error invalid amount")
ErrTxInsufficientFunds = errors.New("Error insufficient funds")
ErrTxUnknownPubKey = errors.New("Error unknown pubkey")
ErrTxInvalidPubKey = errors.New("Error invalid pubkey")
ErrTxRedeclaredPubKey = errors.New("Error redeclared pubkey")
ErrTxInvalidSignature = errors.New("Error invalid signature")
ErrTxInvalidSequence = errors.New("Error invalid sequence")
)
@ -79,6 +82,7 @@ type TxInput struct {
Amount uint64 // Must not exceed account balance
Sequence uint // Must be 1 greater than the last committed TxInput
Signature Signature // Depends on the PubKey type and the whole Tx
PubKey PubKey // Optional, may be nil
}
func (txIn *TxInput) ValidateBasic() error {


+ 2
- 2
cmd/daemon.go View File

@ -26,11 +26,11 @@ type Node struct {
func NewNode() *Node {
// Get BlockStore
blockStoreDB := db_.NewMemDB() // TODO configurable db.
blockStoreDB := db_.GetDB("blockstore")
blockStore := block.NewBlockStore(blockStoreDB)
// Get State
stateDB := db_.NewMemDB() // TODO configurable db.
stateDB := db_.GetDB("state")
state := state_.LoadState(stateDB)
if state == nil {
state = state_.MakeGenesisStateFromFile(stateDB, config.GenesisFile())


+ 10
- 10
cmd/gen_validator.go View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"os"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/state"
@ -10,14 +9,15 @@ import (
func gen_validator() {
// If already exists, bail out.
filename := config.PrivValidatorFile()
if _, err := os.Stat(filename); !os.IsNotExist(err) {
fmt.Printf("Cannot generate new validator, file already exists at %v\n", filename)
}
// Generate private validator
privValidator := state.GenPrivValidator()
privValidator.Save()
fmt.Printf("Generated a new validator at %v\n", filename)
privValidatorJSONBytes := privValidator.JSONBytes()
fmt.Printf(`Generated a new validator!
Paste the following JSON into your %v file
%v
`,
config.PrivValidatorFile(),
string(privValidatorJSONBytes),
)
}

+ 1
- 3
common/cmap.go View File

@ -2,9 +2,7 @@ package common
import "sync"
/*
CMap is a threadsafe map
*/
// CMap is a goroutine-safe map
type CMap struct {
m map[string]interface{}
l sync.Mutex


+ 6
- 0
common/panic.go View File

@ -2,8 +2,14 @@ package common
import (
"fmt"
"os"
)
func Panicf(s string, args ...interface{}) {
panic(fmt.Sprintf(s, args...))
}
func Exitf(s string, args ...interface{}) {
fmt.Printf(s+"\n", args...)
os.Exit(1)
}

+ 84
- 83
config/config.go View File

@ -6,85 +6,12 @@ import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
var rootDir string
func init() {
rootDir = os.Getenv("TMROOT")
if rootDir == "" {
rootDir = os.Getenv("HOME") + "/.tendermint"
}
}
func ConfigFile() string { return rootDir + "/config.json" }
func GenesisFile() string { return rootDir + "/genesis.json" }
func AddrBookFile() string { return rootDir + "/addrbook.json" }
func PrivValidatorFile() string { return rootDir + "/priv_validator.json" }
func DataDir() string { return rootDir + "/data" }
var Config ConfigType
func setFlags(printHelp *bool) {
flag.BoolVar(printHelp, "help", false, "Print this help message.")
flag.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)")
flag.StringVar(&Config.SeedNode, "seed", Config.SeedNode, "Address of seed node")
}
func ParseFlags() {
configFile := ConfigFile()
// try to read configuration. if missing, write default
configBytes, err := ioutil.ReadFile(configFile)
if err != nil {
defaultConfig.write(configFile)
fmt.Println("Config file written to config.json. Please edit & run again")
os.Exit(1)
return
}
// try to parse configuration. on error, die
Config = ConfigType{}
err = json.Unmarshal(configBytes, &Config)
if err != nil {
log.Panicf("Invalid configuration file %s: %v", configFile, err)
}
err = Config.validate()
if err != nil {
log.Panicf("Invalid configuration file %s: %v", configFile, err)
}
// try to parse arg flags, which can override file configuration.
var printHelp bool
setFlags(&printHelp)
flag.Parse()
if printHelp {
flag.PrintDefaults()
os.Exit(0)
}
}
//-----------------------------------------------------------------------------j
// Default configuration
var defaultConfig = ConfigType{
Network: "tendermint_testnet0",
LAddr: "0.0.0.0:0",
SeedNode: "",
Db: DbConfig{
Type: "level",
Dir: DataDir(),
},
Alert: AlertConfig{},
SMTP: SMTPConfig{},
RPC: RPCConfig{
HTTPPort: 8888,
},
}
. "github.com/tendermint/tendermint/common"
)
//-----------------------------------------------------------------------------j
// Configuration types
@ -93,15 +20,15 @@ type ConfigType struct {
Network string
LAddr string
SeedNode string
Db DbConfig
DB DBConfig
Alert AlertConfig
SMTP SMTPConfig
RPC RPCConfig
}
type DbConfig struct {
Type string
Dir string
type DBConfig struct {
Backend string
Dir string
}
type AlertConfig struct {
@ -126,8 +53,6 @@ type RPCConfig struct {
HTTPPort uint
}
//-----------------------------------------------------------------------------j
func (cfg *ConfigType) validate() error {
if cfg.Network == "" {
cfg.Network = defaultConfig.Network
@ -138,8 +63,8 @@ func (cfg *ConfigType) validate() error {
if cfg.SeedNode == "" {
cfg.SeedNode = defaultConfig.SeedNode
}
if cfg.Db.Type == "" {
return errors.New("Db.Type must be set")
if cfg.DB.Backend == "" {
return errors.New("DB.Backend must be set")
}
return nil
}
@ -164,3 +89,79 @@ func (cfg *ConfigType) write(configFile string) {
panic(err)
}
}
//-----------------------------------------------------------------------------
var rootDir string
var defaultConfig ConfigType
func init() {
// Get RootDir
rootDir = os.Getenv("TMROOT")
if rootDir == "" {
rootDir = os.Getenv("HOME") + "/.tendermint"
}
// Compute defaultConfig
defaultConfig = ConfigType{
Network: "tendermint_testnet0",
LAddr: "0.0.0.0:0",
SeedNode: "",
DB: DBConfig{
Backend: "leveldb",
Dir: DataDir(),
},
Alert: AlertConfig{},
SMTP: SMTPConfig{},
RPC: RPCConfig{
HTTPPort: 8888,
},
}
}
func ConfigFile() string { return rootDir + "/config.json" }
func GenesisFile() string { return rootDir + "/genesis.json" }
func AddrBookFile() string { return rootDir + "/addrbook.json" }
func PrivValidatorFile() string { return rootDir + "/priv_validator.json" }
func DataDir() string { return rootDir + "/data" }
var Config ConfigType
func setFlags(printHelp *bool) {
flag.BoolVar(printHelp, "help", false, "Print this help message.")
flag.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)")
flag.StringVar(&Config.SeedNode, "seed", Config.SeedNode, "Address of seed node")
}
func ParseFlags() {
configFile := ConfigFile()
// try to read configuration. if missing, write default
configBytes, err := ioutil.ReadFile(configFile)
if err != nil {
defaultConfig.write(configFile)
fmt.Println("Config file written to config.json. Please edit & run again")
os.Exit(1)
return
}
// try to parse configuration. on error, die
Config = ConfigType{}
err = json.Unmarshal(configBytes, &Config)
if err != nil {
Exitf("Invalid configuration file %s: %v", configFile, err)
}
err = Config.validate()
if err != nil {
Exitf("Invalid configuration file %s: %v", configFile, err)
}
// try to parse arg flags, which can override file configuration.
var printHelp bool
setFlags(&printHelp)
flag.Parse()
if printHelp {
flag.PrintDefaults()
os.Exit(0)
}
}

+ 37
- 0
db/db.go View File

@ -1,5 +1,12 @@
package db
import (
"path"
. "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/config"
)
type DB interface {
Get([]byte) []byte
Set([]byte, []byte)
@ -7,3 +14,33 @@ type DB interface {
Delete([]byte)
DeleteSync([]byte)
}
//-----------------------------------------------------------------------------
// Database types
const DBBackendMemDB = "memdb"
const DBBackendLevelDB = "leveldb"
var dbs = NewCMap()
func GetDB(name string) DB {
db := dbs.Get(name)
if db != nil {
return db.(DB)
}
switch Config.DB.Backend {
case DBBackendMemDB:
db := NewMemDB()
dbs.Set(name, db)
return db
case DBBackendLevelDB:
db, err := NewLevelDB(path.Join(Config.DB.Dir, name+".db"))
if err != nil {
panic(err)
}
dbs.Set(name, db)
return db
default:
panic("Unknown DB backend")
}
}

+ 7
- 2
db/level_db.go View File

@ -3,6 +3,7 @@ package db
import (
"fmt"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
"path"
)
@ -24,7 +25,11 @@ func NewLevelDB(name string) (*LevelDB, error) {
func (db *LevelDB) Get(key []byte) []byte {
res, err := db.db.Get(key, nil)
if err != nil {
panic(err)
if err == errors.ErrNotFound {
return nil
} else {
panic(err)
}
}
return res
}
@ -57,7 +62,7 @@ func (db *LevelDB) DeleteSync(key []byte) {
}
}
func (db *LevelDB) Db() *leveldb.DB {
func (db *LevelDB) DB() *leveldb.DB {
return db.db
}


+ 64
- 14
state/genesis.go View File

@ -1,6 +1,8 @@
package state
import (
"bytes"
"encoding/base64"
"encoding/json"
"io/ioutil"
"time"
@ -13,10 +15,21 @@ import (
"github.com/tendermint/tendermint/merkle"
)
type GenesisAccount struct {
Address string
Amount uint64
}
type GenesisValidator struct {
PubKey string
Amount uint64
UnbondTo []GenesisAccount
}
type GenesisDoc struct {
GenesisTime time.Time
Accounts []*Account
Validators []*ValidatorInfo
Accounts []GenesisAccount
Validators []GenesisValidator
}
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
@ -38,7 +51,7 @@ func MakeGenesisStateFromFile(db db_.DB, genDocFile string) *State {
func MakeGenesisState(db db_.DB, genDoc *GenesisDoc) *State {
if len(genDoc.Validators) == 0 {
panic("Must have some validators")
Exitf("The genesis file has no validators")
}
if genDoc.GenesisTime.IsZero() {
@ -48,22 +61,59 @@ func MakeGenesisState(db db_.DB, genDoc *GenesisDoc) *State {
// Make accounts state tree
accounts := merkle.NewIAVLTree(BasicCodec, AccountCodec, defaultAccountsCacheCapacity, db)
for _, acc := range genDoc.Accounts {
accounts.Set(acc.Address, acc)
address, err := base64.StdEncoding.DecodeString(acc.Address)
if err != nil {
Exitf("Invalid account address: %v", acc.Address)
}
account := &Account{
Address: address,
PubKey: PubKeyNil{},
Sequence: 0,
Balance: acc.Amount,
}
accounts.Set(address, account)
}
// Make validatorInfos state tree
// Make validatorInfos state tree && validators slice
validatorInfos := merkle.NewIAVLTree(BasicCodec, ValidatorInfoCodec, 0, db)
for _, valInfo := range genDoc.Validators {
validatorInfos.Set(valInfo.Address, valInfo)
}
// Make validators
validators := make([]*Validator, len(genDoc.Validators))
for i, valInfo := range genDoc.Validators {
for i, val := range genDoc.Validators {
pubKeyBytes, err := base64.StdEncoding.DecodeString(val.PubKey)
if err != nil {
Exitf("Invalid validator pubkey: %v", val.PubKey)
}
pubKey := ReadBinary(PubKeyEd25519{},
bytes.NewBuffer(pubKeyBytes), new(int64), &err).(PubKeyEd25519)
if err != nil {
Exitf("Invalid validator pubkey: %v", val.PubKey)
}
address := pubKey.Address()
// Make ValidatorInfo
valInfo := &ValidatorInfo{
Address: address,
PubKey: pubKey,
UnbondTo: make([]*TxOutput, len(val.UnbondTo)),
FirstBondHeight: 0,
FirstBondAmount: val.Amount,
}
for i, unbondTo := range val.UnbondTo {
address, err := base64.StdEncoding.DecodeString(unbondTo.Address)
if err != nil {
Exitf("Invalid unbond-to address: %v", unbondTo.Address)
}
valInfo.UnbondTo[i] = &TxOutput{
Address: address,
Amount: unbondTo.Amount,
}
}
validatorInfos.Set(address, valInfo)
// Make validator
validators[i] = &Validator{
Address: valInfo.Address,
PubKey: valInfo.PubKey,
VotingPower: valInfo.FirstBondAmount,
Address: address,
PubKey: pubKey,
VotingPower: val.Amount,
}
}


+ 10
- 5
state/priv_validator.go View File

@ -126,6 +126,14 @@ func (privVal *PrivValidator) Save() {
}
func (privVal *PrivValidator) save() {
jsonBytes := privVal.JSONBytes()
err := ioutil.WriteFile(privVal.filename, jsonBytes, 0700)
if err != nil {
panic(err)
}
}
func (privVal *PrivValidator) JSONBytes() []byte {
privValJSON := PrivValidatorJSON{
Address: base64.StdEncoding.EncodeToString(privVal.Address),
PubKey: base64.StdEncoding.EncodeToString(BinaryBytes(privVal.PubKey)),
@ -134,14 +142,11 @@ func (privVal *PrivValidator) save() {
LastRound: privVal.LastRound,
LastStep: privVal.LastStep,
}
privValJSONBytes, err := json.Marshal(privValJSON)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(privVal.filename, privValJSONBytes, 0700)
privValJSONBytes, err := json.MarshalIndent(privValJSON, "", " ")
if err != nil {
panic(err)
}
return privValJSONBytes
}
// TODO: test


+ 23
- 1
state/state.go View File

@ -108,6 +108,9 @@ func (s *State) Copy() *State {
}
}
// The accounts from the TxInputs must either already have
// account.PubKey.(type) != PubKeyNil, (it must be known),
// or it must be specified in the TxInput. But not both.
func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]*Account, error) {
accounts := map[string]*Account{}
for _, in := range ins {
@ -119,6 +122,20 @@ func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]
if account == nil {
return nil, ErrTxInvalidAddress
}
// PubKey should be present in either "account" or "in"
if _, isNil := account.PubKey.(PubKeyNil); isNil {
if _, isNil := in.PubKey.(PubKeyNil); isNil {
return nil, ErrTxUnknownPubKey
}
if !bytes.Equal(in.PubKey.Address(), account.Address) {
return nil, ErrTxInvalidPubKey
}
account.PubKey = in.PubKey
} else {
if _, isNil := in.PubKey.(PubKeyNil); !isNil {
return nil, ErrTxRedeclaredPubKey
}
}
accounts[string(in.Address)] = account
}
for _, out := range outs {
@ -129,7 +146,12 @@ func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]
account := s.GetAccount(out.Address)
// output account may be nil (new)
if account == nil {
account = NewAccount(NewPubKeyUnknown(out.Address))
account = &Account{
Address: out.Address,
PubKey: PubKeyNil{},
Sequence: 0,
Balance: 0,
}
}
accounts[string(out.Address)] = account
}


+ 25
- 8
state/test.go View File

@ -2,9 +2,11 @@ package state
import (
"bytes"
"encoding/base64"
"sort"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/block"
. "github.com/tendermint/tendermint/common"
db_ "github.com/tendermint/tendermint/db"
@ -24,9 +26,12 @@ func Tempfile(prefix string) (*os.File, string) {
func RandAccount(randBalance bool, minBalance uint64) (*Account, *PrivAccount) {
privAccount := GenPrivAccount()
account := NewAccount(privAccount.PubKey)
account.Sequence = RandUint()
account.Balance = minBalance
account := &Account{
Address: privAccount.PubKey.Address(),
PubKey: privAccount.PubKey,
Sequence: RandUint(),
Balance: minBalance,
}
if randBalance {
account.Balance += uint64(RandUint32())
}
@ -53,20 +58,32 @@ func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *PrivVali
return valInfo, privVal
}
// The first numValidators accounts are validators.
func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numValidators int, randBonded bool, minBonded uint64) (*State, []*PrivAccount, []*PrivValidator) {
db := db_.NewMemDB()
accounts := make([]*Account, numAccounts)
accounts := make([]GenesisAccount, numAccounts)
privAccounts := make([]*PrivAccount, numAccounts)
for i := 0; i < numAccounts; i++ {
account, privAccount := RandAccount(randBalance, minBalance)
accounts[i], privAccounts[i] = account, privAccount
accounts[i] = GenesisAccount{
Address: base64.StdEncoding.EncodeToString(account.Address),
Amount: account.Balance,
}
privAccounts[i] = privAccount
}
validators := make([]*ValidatorInfo, numValidators)
validators := make([]GenesisValidator, numValidators)
privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
valInfo, privVal := RandValidator(randBonded, minBonded)
validators[i] = valInfo
validators[i] = GenesisValidator{
PubKey: base64.StdEncoding.EncodeToString(BinaryBytes(valInfo.PubKey)),
Amount: valInfo.FirstBondAmount,
UnbondTo: []GenesisAccount{
{
Address: base64.StdEncoding.EncodeToString(valInfo.PubKey.Address()),
Amount: valInfo.FirstBondAmount,
},
},
}
privValidators[i] = privVal
}
sort.Sort(PrivValidatorsByAddress(privValidators))


Loading…
Cancel
Save