erigon-pulse/cmd/devnet/services/accounts/faucet.go

238 lines
5.5 KiB
Go
Raw Normal View History

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/cmd/devnet/requests"
)
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.BlockNumber, "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.BlockNumber, "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))
return node.GetBalance(f.contractAddress, requests.BlockNumbers.Latest)
}
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, requests.BlockNumbers.Pending)
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, requests.BlockNumbers.Pending)
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.Name(), 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)
}
}