mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-10 11:41:21 +00:00
ed9db010ea
sharding: separating out transaction receipt functionality Former-commit-id: 1f581a36e282a5a7579f14f613a44946abbdf93d [formerly 58f934a8fb5f23c3e594d70039fc5d99815bc90c] Former-commit-id: f0c9265a84c1f7a09ae3d37a2d4a8f74e2e25b96
201 lines
5.8 KiB
Go
201 lines
5.8 KiB
Go
package mainchain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/ethereum/go-ethereum/sharding"
|
|
"github.com/ethereum/go-ethereum/sharding/contracts"
|
|
)
|
|
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr = crypto.PubkeyToAddress(key.PublicKey)
|
|
accountBalance1001Eth, _ = new(big.Int).SetString("1001000000000000000000", 10)
|
|
)
|
|
|
|
// Verifies that SMCCLient implements the sharding Service inteface.
|
|
var _ = sharding.Service(&SMCClient{})
|
|
|
|
// mockClient is struct to implement the smcClient methods for testing.
|
|
type mockClient struct {
|
|
smc *contracts.SMC
|
|
depositFlag bool
|
|
t *testing.T
|
|
backend *backends.SimulatedBackend
|
|
blockNumber *big.Int
|
|
}
|
|
|
|
// Mirrors the function in the main file, but instead of having a client to perform rpc calls
|
|
// it is replaced by the simulated backend.
|
|
func (m *mockClient) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds time.Duration) error {
|
|
|
|
var receipt *types.Receipt
|
|
|
|
ctxTimeout, cancel := context.WithTimeout(ctx, durationInSeconds*time.Second)
|
|
|
|
for err := error(nil); receipt == nil; receipt, err = m.backend.TransactionReceipt(ctxTimeout, hash) {
|
|
|
|
if err != nil {
|
|
cancel()
|
|
return fmt.Errorf("unable to retrieve transaction: %v", err)
|
|
}
|
|
if ctxTimeout.Err() != nil {
|
|
cancel()
|
|
return fmt.Errorf("transaction timed out, transaction was not able to be mined in the duration: %v", ctxTimeout.Err())
|
|
}
|
|
}
|
|
cancel()
|
|
ctxTimeout.Done()
|
|
log.Info(fmt.Sprintf("Transaction: %s has been mined", hash.Hex()))
|
|
return nil
|
|
}
|
|
|
|
// Creates and send Fake Transactions to the backend to be mined, takes in the context and
|
|
// the current blocknumber as an argument and returns the signed transaction after it has been sent.
|
|
func (m *mockClient) CreateAndSendFakeTx(ctx context.Context) (*types.Transaction, error) {
|
|
tx := types.NewTransaction(m.blockNumber.Uint64(), common.HexToAddress("0x"), nil, 50000, nil, nil)
|
|
signedtx, err := types.SignTx(tx, types.MakeSigner(¶ms.ChainConfig{}, m.blockNumber), key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = m.backend.SendTransaction(ctx, signedtx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to send transaction: %v", err)
|
|
}
|
|
return signedtx, nil
|
|
}
|
|
|
|
func (m *mockClient) Commit() {
|
|
m.backend.Commit()
|
|
m.blockNumber = big.NewInt(m.blockNumber.Int64() + 1)
|
|
}
|
|
|
|
func setup() *backends.SimulatedBackend {
|
|
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance1001Eth}})
|
|
return backend
|
|
}
|
|
|
|
// TestWaitForTransaction tests the WaitForTransaction function in the smcClient, however since for testing we do not have
|
|
// an inmemory rpc server to interact with the simulated backend, we have to define the function in the test rather than
|
|
// in `smc_client.go`.
|
|
// TODO: Test the function in the main file instead of defining it here if a rpc server for testing can be
|
|
// implemented.
|
|
func TestWaitForTransaction_TransactionNotMined(t *testing.T) {
|
|
backend := setup()
|
|
client := &mockClient{backend: backend, blockNumber: big.NewInt(0)}
|
|
ctx := context.Background()
|
|
timeout := time.Duration(1)
|
|
|
|
tx, err := client.CreateAndSendFakeTx(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
receipt, err := client.backend.TransactionReceipt(ctx, tx.Hash())
|
|
if receipt != nil {
|
|
t.Errorf("transaction mined despite backend not being committed: %v", receipt)
|
|
}
|
|
err = client.WaitForTransaction(ctx, tx.Hash(), timeout)
|
|
if err == nil {
|
|
t.Error("transaction is supposed to timeout and return a error")
|
|
}
|
|
|
|
}
|
|
|
|
func TestWaitForTransaction_IsMinedImmediately(t *testing.T) {
|
|
backend := setup()
|
|
client := &mockClient{backend: backend, blockNumber: big.NewInt(0)}
|
|
ctx := context.Background()
|
|
timeout := time.Duration(1)
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
tx, err := client.CreateAndSendFakeTx(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// Tests transaction timing out when the block is mined immediately
|
|
// in the timeout period
|
|
go func() {
|
|
newErr := client.WaitForTransaction(ctx, tx.Hash(), timeout)
|
|
if newErr != nil {
|
|
t.Errorf("transaction timing out despite backend being committed: %v", newErr)
|
|
}
|
|
receipt, err := client.backend.TransactionReceipt(ctx, tx.Hash())
|
|
if err != nil {
|
|
t.Errorf("receipt could not be retrieved:%v", err)
|
|
}
|
|
if receipt == nil {
|
|
t.Error("receipt not found despite transaction being mined")
|
|
}
|
|
wg.Done()
|
|
}()
|
|
client.Commit()
|
|
wg.Wait()
|
|
|
|
}
|
|
func TestWaitForTransaction_TimesOut(t *testing.T) {
|
|
backend := setup()
|
|
client := &mockClient{backend: backend, blockNumber: big.NewInt(0)}
|
|
ctx := context.Background()
|
|
timeout := time.Duration(1)
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
tx, err := client.CreateAndSendFakeTx(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
go func() {
|
|
newErr := client.WaitForTransaction(ctx, tx.Hash(), timeout)
|
|
if newErr == nil {
|
|
t.Error("transaction not timing out despite backend being committed too late")
|
|
}
|
|
client.Commit()
|
|
wg.Done()
|
|
}()
|
|
wg.Wait()
|
|
|
|
}
|
|
func TestWaitForTransaction_IsCancelledWhenParentCtxCancelled(t *testing.T) {
|
|
backend := setup()
|
|
client := &mockClient{backend: backend, blockNumber: big.NewInt(0)}
|
|
ctx := context.Background()
|
|
timeout := time.Duration(1)
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
tx, err := client.CreateAndSendFakeTx(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
newCtx, cancel := context.WithCancel(ctx)
|
|
go func() {
|
|
newErr := client.WaitForTransaction(newCtx, tx.Hash(), timeout)
|
|
if newErr == nil {
|
|
t.Error("no error despite parent context being canceled")
|
|
}
|
|
wg.Done()
|
|
}()
|
|
cancel()
|
|
newCtx.Done()
|
|
|
|
wg.Wait()
|
|
|
|
}
|