diff --git a/account/account.go b/account/account.go index 3178a9241..8e8cd72b5 100644 --- a/account/account.go +++ b/account/account.go @@ -54,8 +54,10 @@ func (acc *Account) Copy() *Account { } func (acc *Account) String() string { - // return fmt.Sprintf("Account{%X:%v C:%v S:%X}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot) - return fmt.Sprintf("Account{%X:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot, acc.Permissions) + if acc == nil { + return "nil-Account" + } + return fmt.Sprintf("Account{%X:%v B:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, acc.Balance, len(acc.Code), acc.StorageRoot, acc.Permissions) } func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) { diff --git a/account/priv_account.go b/account/priv_account.go index 77351e53f..6d633f791 100644 --- a/account/priv_account.go +++ b/account/priv_account.go @@ -12,6 +12,27 @@ type PrivAccount struct { PrivKey PrivKey `json:"priv_key"` } +func (pA *PrivAccount) Generate(index int) *PrivAccount { + newPrivKey := pA.PrivKey.(PrivKeyEd25519).Generate(index) + newPubKey := newPrivKey.PubKey() + newAddress := newPubKey.Address() + return &PrivAccount{ + Address: newAddress, + PubKey: newPubKey, + PrivKey: newPrivKey, + } +} + +func (pA *PrivAccount) Sign(chainID string, o Signable) Signature { + return pA.PrivKey.Sign(SignBytes(chainID, o)) +} + +func (pA *PrivAccount) String() string { + return Fmt("PrivAccount{%X}", pA.Address) +} + +//---------------------------------------- + // Generates a new account with private key. func GenPrivAccount() *PrivAccount { privKeyBytes := new([64]byte) @@ -41,21 +62,18 @@ func GenPrivAccountFromSecret(secret []byte) *PrivAccount { } } -func GenPrivAccountFromKey(privKeyBytes [64]byte) *PrivAccount { - pubKeyBytes := ed25519.MakePublicKey(&privKeyBytes) +func GenPrivAccountFromPrivKeyBytes(privKeyBytes []byte) *PrivAccount { + if len(privKeyBytes) != 64 { + panic(Fmt("Expected 64 bytes but got %v", len(privKeyBytes))) + } + privKeyBytes64 := [64]byte{} + copy(privKeyBytes64[:], privKeyBytes) + pubKeyBytes := ed25519.MakePublicKey(&privKeyBytes64) pubKey := PubKeyEd25519(pubKeyBytes[:]) - privKey := PrivKeyEd25519(privKeyBytes[:]) + privKey := PrivKeyEd25519(privKeyBytes) return &PrivAccount{ Address: pubKey.Address(), PubKey: pubKey, PrivKey: privKey, } } - -func (privAccount *PrivAccount) Sign(chainID string, o Signable) Signature { - return privAccount.PrivKey.Sign(SignBytes(chainID, o)) -} - -func (privAccount *PrivAccount) String() string { - return Fmt("PrivAccount{%X}", privAccount.Address) -} diff --git a/account/priv_key.go b/account/priv_key.go index 791278aba..8c292bd4f 100644 --- a/account/priv_key.go +++ b/account/priv_key.go @@ -28,12 +28,12 @@ var _ = binary.RegisterInterface( // Implements PrivKey type PrivKeyEd25519 []byte -func (privKey PrivKeyEd25519) Sign(msg []byte) Signature { - pubKey := privKey.PubKey().(PubKeyEd25519) - privKeyBytes := new([64]byte) - copy(privKeyBytes[:32], privKey[:]) - copy(privKeyBytes[32:], pubKey[:]) - signatureBytes := ed25519.Sign(privKeyBytes, msg) +func (key PrivKeyEd25519) Sign(msg []byte) Signature { + pubKey := key.PubKey().(PubKeyEd25519) + keyBytes := new([64]byte) + copy(keyBytes[:32], key[:]) + copy(keyBytes[32:], pubKey[:]) + signatureBytes := ed25519.Sign(keyBytes, msg) return SignatureEd25519(signatureBytes[:]) } @@ -46,3 +46,12 @@ func (key PrivKeyEd25519) PubKey() PubKey { func (key PrivKeyEd25519) String() string { return Fmt("PrivKeyEd25519{*****}") } + +// Deterministically generates new priv-key bytes from key. +func (key PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { + newBytes := binary.BinarySha256(struct { + PrivKey []byte + Index int + }{key, index}) + return PrivKeyEd25519(newBytes) +} diff --git a/account/signature.go b/account/signature.go index d51535e7c..29747ebf5 100644 --- a/account/signature.go +++ b/account/signature.go @@ -31,4 +31,4 @@ func (sig SignatureEd25519) IsNil() bool { return false } func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } -func (sig SignatureEd25519) String() string { return fmt.Sprintf("%X", Fingerprint(sig)) } +func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig)) } diff --git a/binary/binary.go b/binary/binary.go index 00b251163..8a4abd6a0 100644 --- a/binary/binary.go +++ b/binary/binary.go @@ -17,11 +17,15 @@ func ReadBinary(o interface{}, r io.Reader, n *int64, err *error) interface{} { rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) if rv.Kind() == reflect.Ptr { if rv.IsNil() { - rv = reflect.New(rt.Elem()) - o = rv.Interface() + // This allows ReadBinaryObject() to return a nil pointer, + // if the value read is nil. + rvPtr := reflect.New(rt) + ReadBinaryPtr(rvPtr.Interface(), r, n, err) + return rvPtr.Elem().Interface() + } else { + readReflectBinary(rv, rt, Options{}, r, n, err) + return o } - readReflectBinary(rv, rt, Options{}, r, n, err) - return o } else { ptrRv := reflect.New(rt) readReflectBinary(ptrRv.Elem(), rt, Options{}, r, n, err) @@ -69,11 +73,15 @@ func ReadJSONObject(o interface{}, object interface{}, err *error) interface{} { rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) if rv.Kind() == reflect.Ptr { if rv.IsNil() { - rv = reflect.New(rt.Elem()) - o = rv.Interface() + // This allows ReadJSONObject() to return a nil pointer + // if the value read is nil. + rvPtr := reflect.New(rt) + ReadJSONObjectPtr(rvPtr.Interface(), object, err) + return rvPtr.Elem().Interface() + } else { + readReflectJSON(rv, rt, object, err) + return o } - readReflectJSON(rv, rt, object, err) - return o } else { ptrRv := reflect.New(rt) readReflectJSON(ptrRv.Elem(), rt, object, err) diff --git a/cmd/logjack/main.go b/cmd/logjack/main.go index cbd6bccd1..f73ac5066 100644 --- a/cmd/logjack/main.go +++ b/cmd/logjack/main.go @@ -14,7 +14,7 @@ import ( ) const Version = "0.0.1" -const sleepSeconds = 1 // Every minute +const sleepSeconds = 1 // Every second // Parse command-line options func parseFlags() (chopSize int64, limitSize int64, version bool, logFiles []string) { diff --git a/cmd/sim_txs/README.md b/cmd/sim_txs/README.md new file mode 100644 index 000000000..1599f7092 --- /dev/null +++ b/cmd/sim_txs/README.md @@ -0,0 +1,10 @@ +Simulate the Economy of Texas. + +```bash +sim_txs --priv-key "PRIV_KEY_HEX" --num-accounts 1000 --remote localhost:46657 +``` + +The above uses the rpc-host to fetch the account state for the account with given priv-key, +then deterministically generates num-accounts more accounts to spread the coins around. + +It's a test utility. diff --git a/cmd/sim_txs/main.go b/cmd/sim_txs/main.go new file mode 100644 index 000000000..6b99a4fb5 --- /dev/null +++ b/cmd/sim_txs/main.go @@ -0,0 +1,173 @@ +package main + +import ( + "bytes" + "encoding/hex" + "flag" + "fmt" + "time" + + acm "github.com/tendermint/tendermint/account" + . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/tendermint/tendermint/types" +) + +const Version = "0.0.1" +const sleepSeconds = 1 // Every second + +// Parse command-line options +func parseFlags() (privKeyHex string, numAccounts int, remote string, version bool) { + flag.StringVar(&privKeyHex, "priv-key", "", "Private key bytes in HEX") + flag.IntVar(&numAccounts, "num-accounts", 1000, "Deterministically generates this many sub-accounts") + flag.StringVar(&remote, "remote", "http://localhost:46657", "Remote RPC host:port") + flag.BoolVar(&version, "version", false, "Version") + flag.Parse() + return +} + +func main() { + + // Read options + privKeyHex, numAccounts, remote, version := parseFlags() + if version { + fmt.Println(Fmt("sim_txs version %v", Version)) + return + } + + // Print args. + // fmt.Println(privKeyHex, numAccounts, remote, version) + + privKeyBytes, err := hex.DecodeString(privKeyHex) + if err != nil { + panic(err) + } + root := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) + fmt.Println("Computed address: %X", root.Address) + + // Get root account. + rootAccount, err := getAccount(remote, root.Address) + if err != nil { + fmt.Println(Fmt("Root account does not exist: %X", root.Address)) + return + } else { + fmt.Println("Root account", rootAccount) + } + + go func() { + // Construct a new send Tx + accounts := make([]*acm.Account, numAccounts) + privAccounts := make([]*acm.PrivAccount, numAccounts) + for i := 0; i < numAccounts; i++ { + privAccounts[i] = root.Generate(i) + account, err := getAccount(remote, privAccounts[i].Address) + if err != nil { + fmt.Println("Error", err) + return + } else { + accounts[i] = account + } + } + for { + sendTx := makeRandomTransaction(rootAccount, root, accounts, privAccounts) + // Broadcast it. + err := broadcastSendTx(remote, sendTx) + if err != nil { + Exit(Fmt("Failed to broadcast SendTx: %v", err)) + return + } + // Broadcast 1 tx! + time.Sleep(10 * time.Millisecond) + } + }() + + // Trap signal + TrapSignal(func() { + fmt.Println("sim_txs shutting down") + }) +} + +func getAccount(remote string, address []byte) (*acm.Account, error) { + // var account *acm.Account = new(acm.Account) + account, err := rpcclient.Call(remote, "get_account", []interface{}{address}, (*acm.Account)(nil)) + if err != nil { + return nil, err + } + if account.(*acm.Account) == nil { + return &acm.Account{Address: address}, nil + } else { + return account.(*acm.Account), nil + } +} + +func broadcastSendTx(remote string, sendTx *types.SendTx) error { + receipt, err := rpcclient.Call(remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil)) + if err != nil { + return err + } + fmt.Println("Broadcast receipt:", receipt) + return nil +} + +func makeRandomTransaction(rootAccount *acm.Account, rootPrivAccount *acm.PrivAccount, accounts []*acm.Account, privAccounts []*acm.PrivAccount) *types.SendTx { + allAccounts := append(accounts, rootAccount) + allPrivAccounts := append(privAccounts, rootPrivAccount) + + // Find accout with the most money + inputBalance := int64(0) + inputAccount := (*acm.Account)(nil) + inputPrivAccount := (*acm.PrivAccount)(nil) + for i, account := range allAccounts { + if account == nil { + continue + } + if inputBalance < account.Balance { + inputBalance = account.Balance + inputAccount = account + inputPrivAccount = allPrivAccounts[i] + } + } + if inputAccount == nil { + Exit("No accounts have any money") + return nil + } + + // Find a selection of accounts to send to + outputAccounts := map[string]*acm.Account{} + for i := 0; i < 2; i++ { + for { + idx := RandInt() % len(accounts) + if bytes.Equal(accounts[idx].Address, inputAccount.Address) { + continue + } + if _, ok := outputAccounts[string(accounts[idx].Address)]; ok { + continue + } + outputAccounts[string(accounts[idx].Address)] = accounts[idx] + break + } + } + + // Construct SendTx + sendTx := types.NewSendTx() + err := sendTx.AddInputWithNonce(inputPrivAccount.PubKey, inputAccount.Balance, inputAccount.Sequence+1) + if err != nil { + panic(err) + } + for _, outputAccount := range outputAccounts { + sendTx.AddOutput(outputAccount.Address, inputAccount.Balance/int64(len(outputAccounts))) + // XXX FIXME??? + outputAccount.Balance += inputAccount.Balance / int64(len(outputAccounts)) + } + + // Sign SendTx + sendTx.SignInput("tendermint_testnet_7", 0, inputPrivAccount) + + // Hack: Listen for events or create a new RPC call for this. + // XXX FIXME + inputAccount.Sequence += 1 + inputAccount.Balance = 0 // FIXME??? + + return sendTx +} diff --git a/crawler/crawl.go b/crawler/crawl.go index 973b27ee8..0920acaee 100644 --- a/crawler/crawl.go +++ b/crawler/crawl.go @@ -3,7 +3,7 @@ package crawler import ( "fmt" "github.com/tendermint/tendermint/binary" - rpctypes "github.com/tendermint/tendermint/rpc/core/types" + ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/core_client" "github.com/tendermint/tendermint/types" "time" @@ -35,7 +35,7 @@ type Node struct { ChainID string BlockHeight int BlockHistory map[int]time.Time // when we saw each block - NetInfo *rpctypes.ResponseNetInfo + NetInfo *ctypes.ResponseNetInfo Validator bool @@ -48,7 +48,7 @@ func (n *Node) Address() string { } // Set the basic status and chain_id info for a node from RPC responses -func (n *Node) SetInfo(status *rpctypes.ResponseStatus, netinfo *rpctypes.ResponseNetInfo) { +func (n *Node) SetInfo(status *ctypes.ResponseStatus, netinfo *ctypes.ResponseNetInfo) { n.LastSeen = time.Now() n.ChainID = status.ChainID n.BlockHeight = status.LatestBlockHeight diff --git a/rpc/core/accounts.go b/rpc/core/accounts.go index 43a356f34..22032a3bd 100644 --- a/rpc/core/accounts.go +++ b/rpc/core/accounts.go @@ -4,7 +4,6 @@ import ( "fmt" acm "github.com/tendermint/tendermint/account" . "github.com/tendermint/tendermint/common" - ptypes "github.com/tendermint/tendermint/permission/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" ) @@ -12,20 +11,12 @@ func GenPrivAccount() (*acm.PrivAccount, error) { return acm.GenPrivAccount(), nil } +// If the account is not known, returns nil, nil. func GetAccount(address []byte) (*acm.Account, error) { cache := mempoolReactor.Mempool.GetCache() account := cache.GetAccount(address) if account == nil { - // XXX: shouldn't we return "account not found"? - account = &acm.Account{ - Address: address, - PubKey: nil, - Sequence: 0, - Balance: 0, - Code: nil, - StorageRoot: nil, - Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress).Permissions, - } + return nil, nil } return account, nil } @@ -34,7 +25,7 @@ func GetStorage(address, key []byte) (*ctypes.ResponseGetStorage, error) { state := consensusState.GetState() account := state.GetAccount(address) if account == nil { - return nil, fmt.Errorf("Unknown address: %X", address) + return nil, fmt.Errorf("UnknownAddress: %X", address) } storageRoot := account.StorageRoot storageTree := state.LoadStorage(storageRoot) @@ -62,7 +53,7 @@ func DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) { state := consensusState.GetState() account := state.GetAccount(address) if account == nil { - return nil, fmt.Errorf("Unknown address: %X", address) + return nil, fmt.Errorf("UnknownAddress: %X", address) } storageRoot := account.StorageRoot storageTree := state.LoadStorage(storageRoot) diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 3a5bbdfb9..90119bb71 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -33,10 +33,10 @@ func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResponseBlockchainInfo, e func GetBlock(height int) (*ctypes.ResponseGetBlock, error) { if height == 0 { - return nil, fmt.Errorf("height must be greater than 0") + return nil, fmt.Errorf("Height must be greater than 0") } if height > blockStore.Height() { - return nil, fmt.Errorf("height must be less than the current blockchain height") + return nil, fmt.Errorf("Height must be less than the current blockchain height") } blockMeta := blockStore.LoadBlockMeta(height) diff --git a/scripts/unsafe_restart_net.sh b/scripts/unsafe_restart_net.sh index dd3f7c616..2c2c5c83c 100755 --- a/scripts/unsafe_restart_net.sh +++ b/scripts/unsafe_restart_net.sh @@ -3,7 +3,7 @@ set -euo pipefail IFS=$'\n\t' debora run -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; killall tendermint; killall logjack" -debora run -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; tendermint unsafe_reset_priv_validator; rm -rf ~/.tendermint/data" +debora run -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; tendermint unsafe_reset_priv_validator; rm -rf ~/.tendermint/data; rm ~/.tendermint/genesis.json" debora run -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; git pull origin develop; make" debora run -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; mkdir -p ~/.tendermint/logs" debora run --bg --label tendermint -- bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; tendermint node | stdinwriter -outpath ~/.tendermint/logs/tendermint.log" diff --git a/types/tx_test.go b/types/tx_test.go index 703f873ed..ba2ac198a 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -70,7 +70,7 @@ func TestCallTxSignable(t *testing.T) { } func TestBondTxSignable(t *testing.T) { - privAccount := account.GenPrivAccountFromKey([64]byte{}) + privAccount := account.GenPrivAccountFromPrivKeyBytes(make([]byte, 64)) bondTx := &BondTx{ PubKey: privAccount.PubKey.(account.PubKeyEd25519), Inputs: []*TxInput{