mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 11:32:09 +00:00
Separating out Transaction Receipt functionality (#186)
sharding: separating out transaction receipt functionality Former-commit-id: 1f581a36e282a5a7579f14f613a44946abbdf93d [formerly 58f934a8fb5f23c3e594d70039fc5d99815bc90c] Former-commit-id: f0c9265a84c1f7a09ae3d37a2d4a8f74e2e25b96
This commit is contained in:
parent
346b9ae8aa
commit
ed9db010ea
@ -151,18 +151,32 @@ func (s *SMCClient) SMCFilterer() *contracts.SMCFilterer {
|
||||
return &s.smc.SMCFilterer
|
||||
}
|
||||
|
||||
// WaitForTransaction waits for transaction to be mined and returns an error if it takes
|
||||
// too long.
|
||||
func (s *SMCClient) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds time.Duration) error {
|
||||
|
||||
ctxTimeout, cancel := context.WithTimeout(ctx, durationInSeconds*time.Second)
|
||||
|
||||
for pending, err := true, error(nil); pending; _, pending, err = s.client.TransactionByHash(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
|
||||
}
|
||||
|
||||
// TransactionReceipt allows an SMCClient to retrieve transaction receipts on
|
||||
// the mainchain by hash.
|
||||
func (s *SMCClient) TransactionReceipt(hash common.Hash) (*types.Receipt, error) {
|
||||
|
||||
for pending, err := true, error(nil); pending; _, pending, err = s.client.TransactionByHash(context.Background(), hash) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to retrieve transaction: %v", err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
log.Info(fmt.Sprintf("Transaction: %s has been mined", hash.Hex()))
|
||||
|
||||
receipt, err := s.client.TransactionReceipt(context.Background(), hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,6 +1,200 @@
|
||||
package mainchain
|
||||
|
||||
import "github.com/ethereum/go-ethereum/sharding"
|
||||
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()
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package notary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
@ -8,7 +9,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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/sharding"
|
||||
"github.com/ethereum/go-ethereum/sharding/contracts"
|
||||
@ -47,6 +50,18 @@ func (s *smcClient) SMCTransactor() *contracts.SMCTransactor {
|
||||
return &s.smc.SMCTransactor
|
||||
}
|
||||
|
||||
func (s *smcClient) SMCFilterer() *contracts.SMCFilterer {
|
||||
return &s.smc.SMCFilterer
|
||||
}
|
||||
|
||||
func (s *smcClient) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *smcClient) TransactionReceipt(hash common.Hash) (*types.Receipt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *smcClient) CreateTXOpts(value *big.Int) (*bind.TransactOpts, error) {
|
||||
txOpts := transactOpts()
|
||||
txOpts.Value = value
|
||||
|
@ -1,6 +1,7 @@
|
||||
package proposer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"testing"
|
||||
@ -47,6 +48,14 @@ func (m *mockNode) SMCTransactor() *contracts.SMCTransactor {
|
||||
return &m.smc.SMCTransactor
|
||||
}
|
||||
|
||||
func (m *mockNode) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockNode) TransactionReceipt(hash common.Hash) (*types.Receipt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockNode) CreateTXOpts(value *big.Int) (*bind.TransactOpts, error) {
|
||||
txOpts := transactOpts()
|
||||
txOpts.Value = value
|
||||
|
Loading…
Reference in New Issue
Block a user