322 lines
9.1 KiB
Go
322 lines
9.1 KiB
Go
|
package rpcdaemontest
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/ecdsa"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"math/big"
|
||
|
"net"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/holiman/uint256"
|
||
|
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
|
||
|
"github.com/ledgerwatch/erigon-lib/gointerfaces/txpool"
|
||
|
"github.com/ledgerwatch/erigon-lib/kv"
|
||
|
"github.com/ledgerwatch/erigon/accounts/abi/bind"
|
||
|
"github.com/ledgerwatch/erigon/accounts/abi/bind/backends"
|
||
|
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/commands/contracts"
|
||
|
"github.com/ledgerwatch/erigon/common"
|
||
|
"github.com/ledgerwatch/erigon/consensus"
|
||
|
"github.com/ledgerwatch/erigon/consensus/ethash"
|
||
|
"github.com/ledgerwatch/erigon/core"
|
||
|
"github.com/ledgerwatch/erigon/core/types"
|
||
|
"github.com/ledgerwatch/erigon/crypto"
|
||
|
"github.com/ledgerwatch/erigon/ethdb/privateapi"
|
||
|
"github.com/ledgerwatch/erigon/params"
|
||
|
"github.com/ledgerwatch/erigon/turbo/snapshotsync"
|
||
|
"github.com/ledgerwatch/erigon/turbo/stages"
|
||
|
"google.golang.org/grpc"
|
||
|
"google.golang.org/grpc/credentials/insecure"
|
||
|
"google.golang.org/grpc/test/bufconn"
|
||
|
)
|
||
|
|
||
|
func CreateTestKV(t *testing.T) kv.RwDB {
|
||
|
s, _, _ := CreateTestSentry(t)
|
||
|
return s.DB
|
||
|
}
|
||
|
|
||
|
type testAddresses struct {
|
||
|
key *ecdsa.PrivateKey
|
||
|
key1 *ecdsa.PrivateKey
|
||
|
key2 *ecdsa.PrivateKey
|
||
|
address common.Address
|
||
|
address1 common.Address
|
||
|
address2 common.Address
|
||
|
}
|
||
|
|
||
|
func makeTestAddresses() testAddresses {
|
||
|
var (
|
||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||
|
key1, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||
|
address1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||
|
address2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||
|
)
|
||
|
|
||
|
return testAddresses{
|
||
|
key: key,
|
||
|
key1: key1,
|
||
|
key2: key2,
|
||
|
address: address,
|
||
|
address1: address1,
|
||
|
address2: address2,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*core.ChainPack) {
|
||
|
addresses := makeTestAddresses()
|
||
|
var (
|
||
|
key = addresses.key
|
||
|
address = addresses.address
|
||
|
address1 = addresses.address1
|
||
|
address2 = addresses.address2
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
gspec = &core.Genesis{
|
||
|
Config: params.AllEthashProtocolChanges,
|
||
|
Alloc: core.GenesisAlloc{
|
||
|
address: {Balance: big.NewInt(9000000000000000000)},
|
||
|
address1: {Balance: big.NewInt(200000000000000000)},
|
||
|
address2: {Balance: big.NewInt(300000000000000000)},
|
||
|
},
|
||
|
GasLimit: 10000000,
|
||
|
}
|
||
|
)
|
||
|
m := stages.MockWithGenesis(t, gspec, key, false)
|
||
|
|
||
|
contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit)
|
||
|
defer contractBackend.Close()
|
||
|
|
||
|
// Generate empty chain to have some orphaned blocks for tests
|
||
|
orphanedChain, err := core.GenerateChain(m.ChainConfig, m.Genesis, m.Engine, m.DB, 5, func(i int, block *core.BlockGen) {
|
||
|
}, true)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
chain, err := getChainInstance(&addresses, m.ChainConfig, m.Genesis, m.Engine, m.DB, contractBackend)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if err = m.InsertChain(orphanedChain); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if err = m.InsertChain(chain); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
return m, chain, []*core.ChainPack{orphanedChain}
|
||
|
}
|
||
|
|
||
|
var chainInstance *core.ChainPack
|
||
|
|
||
|
func getChainInstance(
|
||
|
addresses *testAddresses,
|
||
|
config *params.ChainConfig,
|
||
|
parent *types.Block,
|
||
|
engine consensus.Engine,
|
||
|
db kv.RwDB,
|
||
|
contractBackend *backends.SimulatedBackend,
|
||
|
) (*core.ChainPack, error) {
|
||
|
var err error
|
||
|
if chainInstance == nil {
|
||
|
chainInstance, err = generateChain(addresses, config, parent, engine, db, contractBackend)
|
||
|
}
|
||
|
return chainInstance.Copy(), err
|
||
|
}
|
||
|
|
||
|
func generateChain(
|
||
|
addresses *testAddresses,
|
||
|
config *params.ChainConfig,
|
||
|
parent *types.Block,
|
||
|
engine consensus.Engine,
|
||
|
db kv.RwDB,
|
||
|
contractBackend *backends.SimulatedBackend,
|
||
|
) (*core.ChainPack, error) {
|
||
|
var (
|
||
|
key = addresses.key
|
||
|
key1 = addresses.key1
|
||
|
key2 = addresses.key2
|
||
|
address = addresses.address
|
||
|
address1 = addresses.address1
|
||
|
address2 = addresses.address2
|
||
|
theAddr = common.Address{1}
|
||
|
chainId = big.NewInt(1337)
|
||
|
// this code generates a log
|
||
|
signer = types.LatestSignerForChainID(nil)
|
||
|
)
|
||
|
|
||
|
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, chainId)
|
||
|
transactOpts1, _ := bind.NewKeyedTransactorWithChainID(key1, chainId)
|
||
|
transactOpts2, _ := bind.NewKeyedTransactorWithChainID(key2, chainId)
|
||
|
var poly *contracts.Poly
|
||
|
var tokenContract *contracts.Token
|
||
|
|
||
|
// We generate the blocks without plain state because it's not supported in core.GenerateChain
|
||
|
return core.GenerateChain(config, parent, engine, db, 10, func(i int, block *core.BlockGen) {
|
||
|
var (
|
||
|
txn types.Transaction
|
||
|
txs []types.Transaction
|
||
|
err error
|
||
|
)
|
||
|
|
||
|
ctx := context.Background()
|
||
|
switch i {
|
||
|
case 0:
|
||
|
txn, err = types.SignTx(types.NewTransaction(0, theAddr, uint256.NewInt(1000000000000000), 21000, new(uint256.Int), nil), *signer, key)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
err = contractBackend.SendTransaction(ctx, txn)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
case 1:
|
||
|
txn, err = types.SignTx(types.NewTransaction(1, theAddr, uint256.NewInt(1000000000000000), 21000, new(uint256.Int), nil), *signer, key)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
err = contractBackend.SendTransaction(ctx, txn)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
case 2:
|
||
|
_, txn, tokenContract, err = contracts.DeployToken(transactOpts, contractBackend, address1)
|
||
|
case 3:
|
||
|
txn, err = tokenContract.Mint(transactOpts1, address2, big.NewInt(10))
|
||
|
case 4:
|
||
|
txn, err = tokenContract.Transfer(transactOpts2, address, big.NewInt(3))
|
||
|
case 5:
|
||
|
// Multiple transactions sending small amounts of ether to various accounts
|
||
|
var j uint64
|
||
|
var toAddr common.Address
|
||
|
nonce := block.TxNonce(address)
|
||
|
for j = 1; j <= 32; j++ {
|
||
|
binary.BigEndian.PutUint64(toAddr[:], j)
|
||
|
txn, err = types.SignTx(types.NewTransaction(nonce, toAddr, uint256.NewInt(1_000_000_000_000_000), 21000, new(uint256.Int), nil), *signer, key)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
err = contractBackend.SendTransaction(ctx, txn)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
nonce++
|
||
|
}
|
||
|
case 6:
|
||
|
_, txn, tokenContract, err = contracts.DeployToken(transactOpts, contractBackend, address1)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
txn, err = tokenContract.Mint(transactOpts1, address2, big.NewInt(100))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
// Multiple transactions sending small amounts of ether to various accounts
|
||
|
var j uint64
|
||
|
var toAddr common.Address
|
||
|
for j = 1; j <= 32; j++ {
|
||
|
binary.BigEndian.PutUint64(toAddr[:], j)
|
||
|
txn, err = tokenContract.Transfer(transactOpts2, toAddr, big.NewInt(1))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
}
|
||
|
case 7:
|
||
|
var toAddr common.Address
|
||
|
nonce := block.TxNonce(address)
|
||
|
binary.BigEndian.PutUint64(toAddr[:], 4)
|
||
|
txn, err = types.SignTx(types.NewTransaction(nonce, toAddr, uint256.NewInt(1000000000000000), 21000, new(uint256.Int), nil), *signer, key)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
err = contractBackend.SendTransaction(ctx, txn)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
binary.BigEndian.PutUint64(toAddr[:], 12)
|
||
|
txn, err = tokenContract.Transfer(transactOpts2, toAddr, big.NewInt(1))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
case 8:
|
||
|
_, txn, poly, err = contracts.DeployPoly(transactOpts, contractBackend)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
case 9:
|
||
|
txn, err = poly.DeployAndDestruct(transactOpts, big.NewInt(0))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
txs = append(txs, txn)
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if txs == nil && txn != nil {
|
||
|
txs = append(txs, txn)
|
||
|
}
|
||
|
|
||
|
for _, txn := range txs {
|
||
|
block.AddTx(txn)
|
||
|
}
|
||
|
contractBackend.Commit()
|
||
|
}, true)
|
||
|
}
|
||
|
|
||
|
type IsMiningMock struct{}
|
||
|
|
||
|
func (*IsMiningMock) IsMining() bool { return false }
|
||
|
|
||
|
func CreateTestGrpcConn(t *testing.T, m *stages.MockSentry) (context.Context, *grpc.ClientConn) { //nolint
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
|
||
|
apis := m.Engine.APIs(nil)
|
||
|
if len(apis) < 1 {
|
||
|
t.Fatal("couldn't instantiate Engine api")
|
||
|
}
|
||
|
|
||
|
ethashApi := apis[1].Service.(*ethash.API)
|
||
|
server := grpc.NewServer()
|
||
|
|
||
|
remote.RegisterETHBACKENDServer(server, privateapi.NewEthBackendServer(ctx, nil, m.DB, m.Notifications.Events, snapshotsync.NewBlockReader(), nil, nil, nil, false))
|
||
|
txpool.RegisterTxpoolServer(server, m.TxPoolGrpcServer)
|
||
|
txpool.RegisterMiningServer(server, privateapi.NewMiningServer(ctx, &IsMiningMock{}, ethashApi))
|
||
|
listener := bufconn.Listen(1024 * 1024)
|
||
|
|
||
|
dialer := func() func(context.Context, string) (net.Conn, error) {
|
||
|
go func() {
|
||
|
if err := server.Serve(listener); err != nil {
|
||
|
fmt.Printf("%v\n", err)
|
||
|
}
|
||
|
}()
|
||
|
return func(context.Context, string) (net.Conn, error) {
|
||
|
return listener.Dial()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
conn, err := grpc.DialContext(ctx, "", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer()))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
t.Cleanup(func() {
|
||
|
cancel()
|
||
|
conn.Close()
|
||
|
server.Stop()
|
||
|
})
|
||
|
return ctx, conn
|
||
|
}
|