The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/rosetta/services/tx_format_test.go

297 lines
9.1 KiB

package services
import (
"crypto/ecdsa"
"fmt"
"math/big"
"reflect"
"testing"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/ethereum/go-ethereum/crypto"
wikitypes "github.com/woop-chain/woop/core/types"
"github.com/woop-chain/woop/internal/params"
"github.com/woop-chain/woop/rosetta/common"
stakingTypes "github.com/woop-chain/woop/staking/types"
"github.com/woop-chain/woop/test/helpers"
)
func assertNativeOperationTypeUniquenessInvariant(operations []*types.Operation) error {
foundType := ""
for _, op := range operations {
if _, ok := common.MutuallyExclusiveOperations[op.Type]; !ok {
continue
}
if foundType == "" {
foundType = op.Type
}
if op.Type != foundType {
return fmt.Errorf("found more than 1 type in given set of operations")
}
}
return nil
}
// Note that this test only checks the general format of each type transaction on Woop.
// The detailed operation checks for each type of transaction is done in separate unit tests.
func TestFormatTransactionIntegration(t *testing.T) {
gasLimit := uint64(1e18)
gasUsed := uint64(1e5)
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
receiverKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
testFormatStakingTransaction(t, gasLimit, gasUsed, senderKey, receiverKey)
testFormatPlainTransaction(t, gasLimit, gasUsed, senderKey, receiverKey)
// Note that cross-shard receiver operations/transactions are formatted via
// FormatCrossShardReceiverTransaction, thus, it is not tested here -- but tested on its own.
testFormatCrossShardSenderTransaction(t, gasLimit, gasUsed, senderKey, receiverKey)
}
func testFormatStakingTransaction(
t *testing.T, gasLimit, gasUsed uint64, senderKey, receiverKey *ecdsa.PrivateKey,
) {
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
receiverAddr := crypto.PubkeyToAddress(receiverKey.PublicKey)
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
return stakingTypes.DirectiveDelegate, stakingTypes.Delegate{
DelegatorAddress: senderAddr,
ValidatorAddress: receiverAddr,
Amount: tenOnes,
}
}, senderKey, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
receipt := &wikitypes.Receipt{
Status: wikitypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt, &ContractInfo{}, true)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if len(rosettaTx.Operations) != 3 {
t.Error("Expected 3 operations")
}
if err := assertNativeOperationTypeUniquenessInvariant(rosettaTx.Operations); err != nil {
t.Error(err)
}
if rosettaTx.TransactionIdentifier.Hash != tx.Hash().String() {
t.Error("Invalid transaction")
}
if rosettaTx.Operations[0].Type != common.ExpendGasOperation {
t.Error("Expected 1st operation to be gas type")
}
if rosettaTx.Operations[1].Type != tx.StakingType().String() {
t.Error("Expected 2nd operation to be staking type")
}
if reflect.DeepEqual(rosettaTx.Operations[1].Metadata, map[string]interface{}{}) {
t.Error("Expected staking operation to have some metadata")
}
if !reflect.DeepEqual(rosettaTx.Metadata, map[string]interface{}{}) {
t.Error("Expected transaction to have no metadata")
}
if !reflect.DeepEqual(rosettaTx.Operations[0].Account, senderAccID) {
t.Error("Expected sender to pay gas fee")
}
}
func testFormatPlainTransaction(
t *testing.T, gasLimit, gasUsed uint64, senderKey, receiverKey *ecdsa.PrivateKey,
) {
// Note that post EIP-155 epoch singer is tested in detailed tests.
signer := wikitypes.HomesteadSigner{}
tx, err := helpers.CreateTestTransaction(
signer, 0, 0, 0, 1e18, gasPrice, big.NewInt(1), []byte("test"),
)
if err != nil {
t.Fatal(err.Error())
}
senderAddr, err := tx.SenderAddress()
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
receipt := &wikitypes.Receipt{
Status: wikitypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt, &ContractInfo{}, true)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if len(rosettaTx.Operations) != 3 {
t.Error("Expected 3 operations")
}
if err := assertNativeOperationTypeUniquenessInvariant(rosettaTx.Operations); err != nil {
t.Error(err)
}
if rosettaTx.TransactionIdentifier.Hash != tx.Hash().String() {
t.Error("Invalid transaction")
}
if rosettaTx.Operations[0].Type != common.ExpendGasOperation {
t.Error("Expected 1st operation to be gas")
}
if rosettaTx.Operations[1].Type != common.NativeTransferOperation {
t.Error("Expected 2nd operation to transfer related")
}
if rosettaTx.Operations[1].Metadata != nil {
t.Error("Expected 1st operation to have no metadata")
}
if rosettaTx.Operations[2].Metadata != nil {
t.Error("Expected 2nd operation to have no metadata")
}
if reflect.DeepEqual(rosettaTx.Metadata, map[string]interface{}{}) {
t.Error("Expected transaction to have some metadata")
}
if !reflect.DeepEqual(rosettaTx.Operations[0].Account, senderAccID) {
t.Error("Expected sender to pay gas fee")
}
}
func testFormatCrossShardSenderTransaction(
t *testing.T, gasLimit, gasUsed uint64, senderKey, receiverKey *ecdsa.PrivateKey,
) {
// Note that post EIP-155 epoch singer is tested in detailed tests.
signer := wikitypes.HomesteadSigner{}
tx, err := helpers.CreateTestTransaction(
signer, 0, 1, 0, 1e18, gasPrice, big.NewInt(1), []byte("test"),
)
if err != nil {
t.Fatal(err.Error())
}
senderAddr, err := tx.SenderAddress()
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
receipt := &wikitypes.Receipt{
Status: wikitypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt, &ContractInfo{}, true)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if len(rosettaTx.Operations) != 2 {
t.Error("Expected 2 operations")
}
if err := assertNativeOperationTypeUniquenessInvariant(rosettaTx.Operations); err != nil {
t.Error(err)
}
if rosettaTx.TransactionIdentifier.Hash != tx.Hash().String() {
t.Error("Invalid transaction")
}
if rosettaTx.Operations[0].Type != common.ExpendGasOperation {
t.Error("Expected 1st operation to be gas")
}
if rosettaTx.Operations[1].Type != common.NativeCrossShardTransferOperation {
t.Error("Expected 2nd operation to cross-shard transfer related")
}
if reflect.DeepEqual(rosettaTx.Operations[1].Metadata, map[string]interface{}{}) {
t.Error("Expected 1st operation to have metadata")
}
if reflect.DeepEqual(rosettaTx.Metadata, map[string]interface{}{}) {
t.Error("Expected transaction to have some metadata")
}
if !reflect.DeepEqual(rosettaTx.Operations[0].Account, senderAccID) {
t.Error("Expected sender to pay gas fee")
}
}
func TestFormatCrossShardReceiverTransaction(t *testing.T) {
signer := wikitypes.NewEIP155Signer(params.TestChainConfig.ChainID)
tx, err := helpers.CreateTestTransaction(
signer, 0, 1, 0, 1e18, gasPrice, big.NewInt(1), []byte{},
)
if err != nil {
t.Fatal(err.Error())
}
senderAddr, err := tx.SenderAddress()
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
receiverAccID, rosettaError := newAccountIdentifier(*tx.To())
if rosettaError != nil {
t.Fatal(rosettaError)
}
cxReceipt := &wikitypes.CXReceipt{
TxHash: tx.Hash(),
From: senderAddr,
To: tx.To(),
ShardID: 0,
ToShardID: 1,
Amount: tx.Value(),
}
opMetadata, err := types.MarshalMap(common.CrossShardTransactionOperationMetadata{
From: senderAccID,
To: receiverAccID,
})
if err != nil {
t.Error(err)
}
refCxID := &types.TransactionIdentifier{Hash: tx.Hash().String()}
refOperations := []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{
Index: 0, // There is no gas expenditure for cross-shard payout
},
Type: common.NativeCrossShardTransferOperation,
Status: &common.SuccessOperationStatus.Status,
Account: receiverAccID,
Amount: &types.Amount{
Value: fmt.Sprintf("%v", tx.Value().Uint64()),
Currency: &common.NativeCurrency,
},
Metadata: opMetadata,
},
}
to := tx.ToShardID()
from := tx.ShardID()
refMetadata, err := types.MarshalMap(TransactionMetadata{
CrossShardIdentifier: refCxID,
ToShardID: &to,
FromShardID: &from,
})
refRosettaTx := &types.Transaction{
TransactionIdentifier: refCxID,
Operations: refOperations,
Metadata: refMetadata,
}
rosettaTx, rosettaError := FormatCrossShardReceiverTransaction(cxReceipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(rosettaTx, refRosettaTx) {
t.Errorf("Expected transaction to be %v not %v", refRosettaTx, rosettaTx)
}
if err := assertNativeOperationTypeUniquenessInvariant(rosettaTx.Operations); err != nil {
t.Error(err)
}
}