erigon-pulse/cmd/devnet/services/accounts/faucet.go
battlmonstr 3698e7f476
devnet: configuration fixes (#8592)
* fix "genesis hash does not match" when dev nodes connect  
The "dev" nodes need to have the same --miner.etherbase in order to
generate the same genesis ExtraData by DeveloperGenesisBlock(). Override
DevnetEtherbase global var that's used if --miner.etherbase is not
passed. (for NonBlockProducer case)

* fix missing private key for the hardcoded DevnetEtherbase  
Fixes panic if SigKey is not found. Bor non-producers will use a default
`DevnetEtherbase` while Dev nodes modify it. Save hardcoded
DevnetEtherbase/DevnetSignPrivateKey into accounts so that SigKey can
recover it.

* refactor devnet.node to contain Node config  
This avoids interface{} type casts and fixes an error with
Heimdall.validatorSet == nil

* add connection retries to rpcCall and Subscribe of requestGenerator  
Fixes "connection refused" errors due to node not ready to handle early
RPC requests.

* fix deadlock in Heimdall.NodeStarted

* fix GetBlockByNumber
Fixes "cannot unmarshal string into Go struct field body.transactions of
type jsonrpc.RPCTransaction"

* demote "no of blocks on childchain is less than confirmations
required" to Info (#8626)

* demote "mismatched pending subpool size" to Debug (#8615)

* revert wiggle testing code
2023-11-01 11:08:47 +01:00

243 lines
5.5 KiB
Go

package accounts
import (
"context"
"fmt"
"math/big"
"strings"
"sync"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/accounts/abi/bind"
"github.com/ledgerwatch/erigon/cmd/devnet/accounts"
"github.com/ledgerwatch/erigon/cmd/devnet/blocks"
"github.com/ledgerwatch/erigon/cmd/devnet/contracts"
"github.com/ledgerwatch/erigon/cmd/devnet/devnet"
"github.com/ledgerwatch/erigon/rpc"
)
type Faucet struct {
sync.Mutex
chainName string
source *accounts.Account
transactOpts *bind.TransactOpts
contractAddress libcommon.Address
contract *contracts.Faucet
deployer *deployer
}
type deployer struct {
sync.WaitGroup
faucet *Faucet
}
func (d *deployer) deploy(ctx context.Context, node devnet.Node) {
logger := devnet.Logger(ctx)
// deploy the contract and get the contract handler
deployCtx := devnet.WithCurrentNode(ctx, node)
waiter, deployCancel := blocks.BlockWaiter(deployCtx, contracts.DeploymentChecker)
defer deployCancel()
address, transaction, contract, err := contracts.DeployWithOps(deployCtx, d.faucet.transactOpts, contracts.DeployFaucet)
if err != nil {
d.faucet.Lock()
defer d.faucet.Unlock()
d.faucet.deployer = nil
d.faucet.transactOpts = nil
logger.Error("failed to deploy faucet", "chain", d.faucet.chainName, "err", err)
return
}
block, err := waiter.Await(transaction.Hash())
if err != nil {
d.faucet.Lock()
defer d.faucet.Unlock()
d.faucet.deployer = nil
d.faucet.transactOpts = nil
logger.Error("failed to deploy faucet", "chain", d.faucet.chainName, "err", err)
return
}
logger.Info("Faucet deployed", "chain", d.faucet.chainName, "block", block.Number, "addr", address)
d.faucet.contractAddress = address
d.faucet.contract = contract
// make the amount received a fraction of the source
//if sbal, err := node.GetBalance(f.source, requests.BlockNumbers.Latest); err == nil {
// fmt.Println(f.source, sbal)
//}
waiter, receiveCancel := blocks.BlockWaiter(deployCtx, blocks.CompletionChecker)
defer receiveCancel()
received, receiveHash, err := d.faucet.Receive(deployCtx, d.faucet.source, 20000)
if err != nil {
logger.Error("Failed to receive faucet funds", "err", err)
return
}
block, err = waiter.Await(receiveHash)
if err != nil {
d.faucet.Lock()
defer d.faucet.Unlock()
d.faucet.deployer = nil
d.faucet.transactOpts = nil
logger.Error("failed to deploy faucet", "chain", d.faucet.chainName, "err", err)
return
}
logger.Info("Faucet funded", "chain", d.faucet.chainName, "block", block.Number, "addr", address, "received", received)
d.faucet.Lock()
defer d.faucet.Unlock()
d.faucet.deployer = nil
d.Done()
}
func NewFaucet(chainName string, source *accounts.Account) *Faucet {
return &Faucet{
chainName: chainName,
source: source,
}
}
func (f *Faucet) Start(context context.Context) error {
return nil
}
func (f *Faucet) Stop() {}
func (f *Faucet) Address() libcommon.Address {
return f.contractAddress
}
func (f *Faucet) Contract() *contracts.Faucet {
return f.contract
}
func (f *Faucet) Source() *accounts.Account {
return f.source
}
func (f *Faucet) Balance(ctx context.Context) (*big.Int, error) {
f.Lock()
deployer := f.deployer
f.Unlock()
if deployer != nil {
f.deployer.Wait()
}
node := devnet.SelectBlockProducer(devnet.WithCurrentNetwork(ctx, f.chainName))
if node == nil {
return nil, fmt.Errorf("%s has no block producers", f.chainName)
}
return node.GetBalance(f.contractAddress, rpc.LatestBlock)
}
func (f *Faucet) Send(ctx context.Context, destination *accounts.Account, eth float64) (*big.Int, libcommon.Hash, error) {
f.Lock()
deployer := f.deployer
f.Unlock()
if deployer != nil {
f.deployer.Wait()
}
if f.transactOpts == nil {
return nil, libcommon.Hash{}, fmt.Errorf("Faucet not initialized")
}
node := devnet.SelectNode(ctx)
count, err := node.GetTransactionCount(f.source.Address, rpc.PendingBlock)
if err != nil {
return nil, libcommon.Hash{}, err
}
f.transactOpts.Nonce = count
amount := accounts.EtherAmount(eth)
trn, err := f.contract.Send(f.transactOpts, destination.Address, amount)
if err != nil {
return nil, libcommon.Hash{}, err
}
return amount, trn.Hash(), err
}
func (f *Faucet) Receive(ctx context.Context, source *accounts.Account, eth float64) (*big.Int, libcommon.Hash, error) {
node := devnet.SelectNode(ctx)
transactOpts, err := bind.NewKeyedTransactorWithChainID(source.SigKey(), node.ChainID())
if err != nil {
return nil, libcommon.Hash{}, err
}
count, err := node.GetTransactionCount(f.source.Address, rpc.PendingBlock)
if err != nil {
return nil, libcommon.Hash{}, err
}
transactOpts.Nonce = count
transactOpts.Value = accounts.EtherAmount(eth)
trn, err := (&contracts.FaucetRaw{Contract: f.contract}).Transfer(transactOpts)
if err != nil {
return nil, libcommon.Hash{}, err
}
return transactOpts.Value, trn.Hash(), nil
}
func (f *Faucet) NodeCreated(ctx context.Context, node devnet.Node) {
}
func (f *Faucet) NodeStarted(ctx context.Context, node devnet.Node) {
logger := devnet.Logger(ctx)
if strings.HasPrefix(node.GetName(), f.chainName) && node.IsBlockProducer() {
f.Lock()
defer f.Unlock()
if f.transactOpts != nil {
return
}
var err error
f.transactOpts, err = bind.NewKeyedTransactorWithChainID(f.source.SigKey(), node.ChainID())
if err != nil {
logger.Error("failed to get transaction ops", "address", f.source, "err", err)
return
}
f.deployer = &deployer{
faucet: f,
}
f.deployer.Add(1)
go f.deployer.deploy(ctx, node)
}
}