Browse Source

Added sim_txs draft

pull/113/head
Jae Kwon 10 years ago
parent
commit
109a3c2dd3
13 changed files with 260 additions and 49 deletions
  1. +4
    -2
      account/account.go
  2. +29
    -11
      account/priv_account.go
  3. +15
    -6
      account/priv_key.go
  4. +1
    -1
      account/signature.go
  5. +16
    -8
      binary/binary.go
  6. +1
    -1
      cmd/logjack/main.go
  7. +10
    -0
      cmd/sim_txs/README.md
  8. +173
    -0
      cmd/sim_txs/main.go
  9. +3
    -3
      crawler/crawl.go
  10. +4
    -13
      rpc/core/accounts.go
  11. +2
    -2
      rpc/core/blocks.go
  12. +1
    -1
      scripts/unsafe_restart_net.sh
  13. +1
    -1
      types/tx_test.go

+ 4
- 2
account/account.go View File

@ -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) {


+ 29
- 11
account/priv_account.go View File

@ -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)
}

+ 15
- 6
account/priv_key.go View File

@ -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)
}

+ 1
- 1
account/signature.go View File

@ -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)) }

+ 16
- 8
binary/binary.go View File

@ -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)


+ 1
- 1
cmd/logjack/main.go View File

@ -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) {


+ 10
- 0
cmd/sim_txs/README.md View File

@ -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.

+ 173
- 0
cmd/sim_txs/main.go View File

@ -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
}

+ 3
- 3
crawler/crawl.go View File

@ -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


+ 4
- 13
rpc/core/accounts.go View File

@ -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)


+ 2
- 2
rpc/core/blocks.go View File

@ -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)


+ 1
- 1
scripts/unsafe_restart_net.sh View File

@ -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"


+ 1
- 1
types/tx_test.go View File

@ -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{


Loading…
Cancel
Save