Files
atomic-swap/ethereum/block/wait_for_receipt_test.go

113 lines
3.8 KiB
Go

// Copyright 2023 The AthanorLabs/atomic-swap Authors
// SPDX-License-Identifier: LGPL-3.0-only
package block
import (
"context"
"crypto/ecdsa"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/tests"
)
// stampChecker collects all the pieces of data needed to create a transaction that calls
// check_stamp on the UTContract for unit tests.
type stampChecker struct {
t *testing.T
ec *ethclient.Client
ctx context.Context
chainID *big.Int
fromKey *ecdsa.PrivateKey
contract *UTContract
}
// checkStamp performs the setup of creating a check_stamp transaction so unit tests
// can concentrate on the return values of WaitForReceipt.
func (c *stampChecker) checkStamp(epochTimeStamp int64) (*ethtypes.Receipt, error) {
auth, err := bind.NewKeyedTransactorWithChainID(c.fromKey, c.chainID)
require.NoError(c.t, err)
tx, err := c.contract.CheckStamp(auth, big.NewInt(epochTimeStamp))
require.NoError(c.t, err)
return WaitForReceipt(c.ctx, c.ec, tx.Hash())
}
// curBlockHeader is a test helper function to get the latest block header
func (c *stampChecker) curBlockHeader() *ethtypes.Header {
hdr, err := c.ec.HeaderByNumber(c.ctx, nil)
require.NoError(c.t, err)
return hdr
}
// createStampChecker deploys the unit test contract (UTContract)
func createStampChecker(t *testing.T) *stampChecker {
ec, chainID := tests.NewEthClient(t)
ctx := context.Background()
fromKey := tests.GetTestKeyByIndex(t, 0)
auth, err := bind.NewKeyedTransactorWithChainID(fromKey, chainID)
require.NoError(t, err)
_, tx, contract, err := DeployUTContract(auth, ec)
require.NoError(t, err)
_, err = WaitForReceipt(ctx, ec, tx.Hash())
require.NoError(t, err)
return &stampChecker{
t: t,
ec: ec,
ctx: ctx,
chainID: chainID,
fromKey: fromKey,
contract: contract,
}
}
// Test WaitForReceipt with 2 transactions that are successfully mined:
// 1. contract creation
// 2. calling check_stamp with a value >= the mined block's timestamp
func TestWaitForReceipt_success(t *testing.T) {
checker := createStampChecker(t)
blockBefore := checker.curBlockHeader()
futureTime := time.Now().Add(time.Hour).Unix()
receipt, err := checker.checkStamp(futureTime)
require.NoError(t, err)
txBlockNum := receipt.BlockNumber
require.Greater(t, txBlockNum.Uint64(), blockBefore.Number.Uint64())
}
// Test our ErrorFromBlock method. This test is designed to create a transaction that will
// successfully be created and sent to the network, but then subsequently fail when it is
// mined into a block.
func TestWaitForReceipt_failWhenTransactionIsMined(t *testing.T) {
checker := createStampChecker(t)
// One second in the future rounding up
oneSecInFuture := time.Now().Add(1500 * time.Millisecond)
// We don't want a race condition between getting the current block and creating the
// check_stamp transaction. The transaction creation needs to be done when the
// current block timestamp is exactly equal to the time we pass. That way transaction
// creation will pass, but mining will fail. By waiting for the next block, we have
// a full second to create the new transaction and avoid any race condition.
hdr, err := WaitForEthBlockAfterTimestamp(checker.ctx, checker.ec, oneSecInFuture)
require.NoError(t, err)
_, err = checker.checkStamp(int64(hdr.Time))
require.Error(t, err)
// Ensure that we got the expected error
require.Contains(t, err.Error(), "revert block.timestamp was not less than stamp")
// Ensure that the expected error happened when the transaction was mined and not earlier
require.Contains(t, err.Error(), "failed transaction included in block")
}