// Copyright 2018 The dexon-consensus Authors
// This file is part of the dexon-consensus library.
//
// The dexon-consensus library is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// The dexon-consensus library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the dexon-consensus library. If not, see
// <http://www.gnu.org/licenses/>.
// A simple monkey that sends random transactions into the network.
package monkey
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"math/rand"
"os"
"time"
"github.com/dexon-foundation/dexon/cmd/zoo/client"
"github.com/dexon-foundation/dexon/crypto"
)
var config *MonkeyConfig
type MonkeyConfig struct {
Key string
Endpoint string
N int
Gambler bool
Feeder bool
Batch bool
Sleep int
Timeout int
}
func Init(cfg *MonkeyConfig) {
config = cfg
}
type Monkey struct {
client.Client
source *ecdsa.PrivateKey
keys []*ecdsa.PrivateKey
timer <-chan time.Time
}
func New(ep string, source *ecdsa.PrivateKey, num int, timeout time.Duration) *Monkey {
client, err := client.New(ep)
if err != nil {
panic(err)
}
file := func() *os.File {
for i := 0; i < 100; i++ {
name := fmt.Sprintf("zoo-%d.keys", i)
file, err := os.OpenFile(name,
os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600)
if err != nil {
continue
}
fmt.Printf("Save keys to file %s\n", name)
return file
}
return nil
}()
if file == nil {
panic(fmt.Errorf("Failed to create file for zoo keys"))
}
defer file.Close()
var keys []*ecdsa.PrivateKey
for i := 0; i < num; i++ {
key, err := crypto.GenerateKey()
if err != nil {
panic(err)
}
_, err = file.Write([]byte(hex.EncodeToString(crypto.FromECDSA(key)) + "\n"))
if err != nil {
panic(err)
}
keys = append(keys, key)
}
monkey := &Monkey{
Client: *client,
source: source,
keys: keys,
}
if timeout > 0 {
monkey.timer = time.After(timeout * time.Second)
}
return monkey
}
func (m *Monkey) Distribute() {
fmt.Println("Distributing DEX to random accounts ...")
address := crypto.PubkeyToAddress(m.source.PublicKey)
nonce, err := m.PendingNonceAt(context.Background(), address)
if err != nil {
panic(err)
}
ctxs := make([]*client.TransferContext, len(m.keys))
for i, key := range m.keys {
address := crypto.PubkeyToAddress(key.PublicKey)
amount := new(big.Int)
amount.SetString("1000000000000000000", 10)
ctxs[i] = &client.TransferContext{
Key: m.source,
ToAddress: address,
Amount: amount,
Nonce: nonce,
Gas: 21000,
}
nonce++
}
m.BatchTransfer(ctxs)
time.Sleep(20 * time.Second)
}
func (m *Monkey) Crazy() uint64 {
fmt.Println("Performing random transfers ...")
nonce := uint64(0)
loop:
for {
ctxs := make([]*client.TransferContext, len(m.keys))
for i, key := range m.keys {
to := crypto.PubkeyToAddress(m.keys[rand.Int()%len(m.keys)].PublicKey)
amount := new(big.Int)
amount.SetString(fmt.Sprintf("%d0000000000000", rand.Intn(10)+1), 10)
ctx := &client.TransferContext{
Key: key,
ToAddress: to,
Amount: amount,
Nonce: nonce,
Gas: 21000,
}
if config.Batch {
ctxs[i] = ctx
} else {
m.Transfer(ctx)
}
}
if config.Batch {
m.BatchTransfer(ctxs)
}
fmt.Printf("Sent %d transactions, nonce = %d\n", len(m.keys), nonce)
if m.timer != nil {
select {
case <-m.timer:
break loop
default:
}
}
nonce++
time.Sleep(time.Duration(config.Sleep) * time.Millisecond)
}
return nonce
}
func (m *Monkey) Keys() []*ecdsa.PrivateKey {
return m.keys
}
func Exec() (*Monkey, uint64) {
privKey, err := crypto.LoadECDSA(config.Key)
if err != nil {
panic(err)
}
m := New(config.Endpoint, privKey, config.N, time.Duration(config.Timeout))
m.Distribute()
var finalNonce uint64
if config.Gambler {
finalNonce = m.Gamble()
} else if config.Feeder {
finalNonce = m.Feed()
} else {
finalNonce = m.Crazy()
}
return m, finalNonce
}