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_operation_test.go

1194 lines
40 KiB

package services
import (
"fmt"
"math/big"
"reflect"
"testing"
"github.com/coinbase/rosetta-sdk-go/types"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
hmytypes "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/hmy"
internalCommon "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/rosetta/common"
"github.com/harmony-one/harmony/staking"
stakingTypes "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/harmony/test/helpers"
)
func TestGetStakingOperationsFromCreateValidator(t *testing.T) {
gasLimit := uint64(1e18)
createValidatorTxDescription := stakingTypes.Description{
Name: "SuperHero",
Identity: "YouWouldNotKnow",
Website: "Secret Website",
SecurityContact: "LicenseToKill",
Details: "blah blah blah",
}
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
fromKey, _ := crypto.GenerateKey()
return stakingTypes.DirectiveCreateValidator, stakingTypes.CreateValidator{
Description: createValidatorTxDescription,
MinSelfDelegation: tenOnes,
MaxTotalDelegation: twelveOnes,
ValidatorAddress: crypto.PubkeyToAddress(fromKey.PublicKey),
Amount: tenOnes,
}
}, nil, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
metadata, err := helpers.GetMessageFromStakingTx(tx)
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)
}
gasUsed := uint64(1e5)
gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasUsed)))
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusSuccessful, // Failed staking transaction are never saved on-chain
GasUsed: gasUsed,
}
refOperations := newNativeOperationsWithGas(gasFee, senderAccID)
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: negativeBigValue(tenOnes),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
operations, rosettaError := GetNativeOperationsFromStakingTransaction(tx, receipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetSideEffectOperationsFromValueMap(t *testing.T) {
testAcc1 := crypto.PubkeyToAddress(internalCommon.MustGeneratePrivateKey().PublicKey)
testAcc2 := crypto.PubkeyToAddress(internalCommon.MustGeneratePrivateKey().PublicKey)
testAmount1 := big.NewInt(12000)
testAmount2 := big.NewInt(10000)
testPayouts := map[ethcommon.Address]*big.Int{
testAcc1: testAmount1,
testAcc2: testAmount2,
}
testType := common.GenesisFundsOperation
ops, rosettaError := getSideEffectOperationsFromValueMap(testPayouts, testType, nil)
if rosettaError != nil {
t.Fatal(rosettaError)
}
for i, op := range ops {
if int64(i) != op.OperationIdentifier.Index {
t.Errorf("expected operation %v to have operation index %v", i, i)
}
address, err := getAddress(op.Account)
if err != nil {
t.Fatal(err)
}
if value, ok := testPayouts[address]; !ok {
t.Errorf("operation %v has address that is not in test map", i)
} else if value.String() != op.Amount.Value {
t.Errorf("operation %v has wrong value (%v != %v)", i, value.String(), op.Amount.Value)
}
if op.Type != testType {
t.Errorf("operation %v has wrong type", i)
}
if len(op.RelatedOperations) != 0 {
t.Errorf("operation %v has related operations", i)
}
if types.Hash(op.Amount.Currency) != common.NativeCurrencyHash {
t.Errorf("operation %v has wrong currency", i)
}
}
if err := assertNativeOperationTypeUniquenessInvariant(ops); err != nil {
t.Error(err)
}
testStartingIndex := int64(12)
ops, rosettaError = getSideEffectOperationsFromValueMap(testPayouts, testType, &testStartingIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
for i, op := range ops {
if int64(i)+testStartingIndex != op.OperationIdentifier.Index {
t.Errorf("expected operation %v to have operation index %v", i, int64(i)+testStartingIndex)
}
address, err := getAddress(op.Account)
if err != nil {
t.Fatal(err)
}
if value, ok := testPayouts[address]; !ok {
t.Errorf("operation %v has address that is not in test map", i)
} else if value.String() != op.Amount.Value {
t.Errorf("operation %v has wrong value (%v != %v)", i, value.String(), op.Amount.Value)
}
if op.Type != testType {
t.Errorf("operation %v has wrong type", i)
}
if len(op.RelatedOperations) != 0 {
t.Errorf("operation %v has related operations", i)
}
if types.Hash(op.Amount.Currency) != common.NativeCurrencyHash {
t.Errorf("operation %v has wrong currency", i)
}
}
if err := assertNativeOperationTypeUniquenessInvariant(ops); err != nil {
t.Error(err)
}
}
func TestGetStakingOperationsFromDelegate(t *testing.T) {
gasLimit := uint64(1e18)
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
validatorKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
validatorAddr := crypto.PubkeyToAddress(validatorKey.PublicKey)
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
return stakingTypes.DirectiveDelegate, stakingTypes.Delegate{
DelegatorAddress: senderAddr,
ValidatorAddress: validatorAddr,
Amount: tenOnes,
}
}, senderKey, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
metadata, err := helpers.GetMessageFromStakingTx(tx)
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
senderAccIDWithSubAccount, rosettaError := newAccountIdentifierWithSubAccount(senderAddr, validatorAddr,
map[string]interface{}{SubAccountMetadataKey: Delegation})
if rosettaError != nil {
t.Fatal(rosettaError)
}
gasUsed := uint64(1e5)
gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasUsed)))
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusSuccessful, // Failed staking transaction are never saved on-chain
GasUsed: gasUsed,
}
refOperations := newNativeOperationsWithGas(gasFee, senderAccID)
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: negativeBigValue(tenOnes),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 2},
RelatedOperations: []*types.OperationIdentifier{
{
Index: 1,
},
},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccIDWithSubAccount,
Amount: &types.Amount{
Value: tenOnes.String(),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
operations, rosettaError := GetNativeOperationsFromStakingTransaction(tx, receipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetSideEffectOperationsFromUndelegationPayouts(t *testing.T) {
startingOperationIndex := int64(0)
undelegationPayouts := hmy.UndelegationPayouts{}
delegator := ethcommon.HexToAddress("0xB5f440B5c6215eEDc1b2E12b4b964fa31f7afa7d")
validator1 := ethcommon.HexToAddress("0x3b8DE43c8F30D3C387840681FED67783f93f1F94")
validator2 := ethcommon.HexToAddress("0xB5f440B5c6215eEDc1b2E12b4b964fa31f7afa7d")
undelegationPayouts[delegator] = make(map[ethcommon.Address]*big.Int)
undelegationPayouts[delegator][validator1] = new(big.Int).SetInt64(4000)
undelegationPayouts[delegator][validator2] = new(big.Int).SetInt64(5000)
operations, err := GetSideEffectOperationsFromUndelegationPayouts(undelegationPayouts, &startingOperationIndex)
if err != nil {
t.Fatal(err)
}
receiverAccId, err := newAccountIdentifier(delegator)
if err != nil {
t.Fatal(err)
}
reveiverSubAccId1, err := newAccountIdentifierWithSubAccount(delegator, validator1, map[string]interface{}{
SubAccountMetadataKey: UnDelegation,
})
if err != nil {
t.Fatal(err)
}
reveiverSubAccId2, err := newAccountIdentifierWithSubAccount(delegator, validator2, map[string]interface{}{
SubAccountMetadataKey: UnDelegation,
})
if err != nil {
t.Fatal(err)
}
refOperations := []*types.Operation{}
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 0},
Type: UndelegationPayout,
Status: common.SuccessOperationStatus.Status,
Account: receiverAccId,
Amount: &types.Amount{
Value: fmt.Sprintf("9000"),
Currency: &common.NativeCurrency,
},
}, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
RelatedOperations: []*types.OperationIdentifier{
&types.OperationIdentifier{
Index: 0,
},
},
Type: UndelegationPayout,
Status: common.SuccessOperationStatus.Status,
Account: reveiverSubAccId1,
Amount: &types.Amount{
Value: fmt.Sprintf("-4000"),
Currency: &common.NativeCurrency,
},
}, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 2},
RelatedOperations: []*types.OperationIdentifier{
&types.OperationIdentifier{
Index: 0,
},
},
Type: UndelegationPayout,
Status: common.SuccessOperationStatus.Status,
Account: reveiverSubAccId2,
Amount: &types.Amount{
Value: fmt.Sprintf("-5000"),
Currency: &common.NativeCurrency,
},
})
if !reflect.DeepEqual(refOperations, operations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
}
func TestGetStakingOperationsFromUndelegate(t *testing.T) {
gasLimit := uint64(1e18)
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
validatorKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
validatorAddr := crypto.PubkeyToAddress(validatorKey.PublicKey)
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
return stakingTypes.DirectiveUndelegate, stakingTypes.Undelegate{
DelegatorAddress: senderAddr,
ValidatorAddress: validatorAddr,
Amount: tenOnes,
}
}, senderKey, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
metadata, err := helpers.GetMessageFromStakingTx(tx)
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifierWithSubAccount(senderAddr, validatorAddr, map[string]interface{}{
SubAccountMetadataKey: Delegation,
})
if rosettaError != nil {
t.Fatal(rosettaError)
}
receiverAccId, rosettaError := newAccountIdentifierWithSubAccount(senderAddr, validatorAddr, map[string]interface{}{
SubAccountMetadataKey: UnDelegation,
})
if rosettaError != nil {
t.Fatal(rosettaError)
}
gasUsed := uint64(1e5)
gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasUsed)))
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusSuccessful, // Failed staking transaction are never saved on-chain
GasUsed: gasUsed,
}
refOperations := newNativeOperationsWithGas(gasFee, senderAccID)
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: fmt.Sprintf("0"),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 2},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: receiverAccId,
Amount: &types.Amount{
Value: fmt.Sprintf("0"),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
operations, rosettaError := GetNativeOperationsFromStakingTransaction(tx, receipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetStakingOperationsFromCollectRewards(t *testing.T) {
gasLimit := uint64(1e18)
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
return stakingTypes.DirectiveCollectRewards, stakingTypes.CollectRewards{
DelegatorAddress: senderAddr,
}
}, senderKey, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
metadata, err := helpers.GetMessageFromStakingTx(tx)
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
gasUsed := uint64(1e5)
gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasUsed)))
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusSuccessful, // Failed staking transaction are never saved on-chain
GasUsed: gasUsed,
Logs: []*hmytypes.Log{
{
Address: senderAddr,
Topics: []ethcommon.Hash{staking.CollectRewardsTopic},
Data: tenOnes.Bytes(),
},
},
}
refOperations := newNativeOperationsWithGas(gasFee, senderAccID)
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: fmt.Sprintf("%v", tenOnes.Uint64()),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
operations, rosettaError := GetNativeOperationsFromStakingTransaction(tx, receipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetStakingOperationsFromEditValidator(t *testing.T) {
gasLimit := uint64(1e18)
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
tx, err := helpers.CreateTestStakingTransaction(func() (stakingTypes.Directive, interface{}) {
return stakingTypes.DirectiveEditValidator, stakingTypes.EditValidator{
ValidatorAddress: senderAddr,
}
}, senderKey, 0, gasLimit, gasPrice)
if err != nil {
t.Fatal(err.Error())
}
metadata, err := helpers.GetMessageFromStakingTx(tx)
if err != nil {
t.Fatal(err.Error())
}
senderAccID, rosettaError := newAccountIdentifier(senderAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
gasUsed := uint64(1e5)
gasFee := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasUsed)))
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusSuccessful, // Failed staking transaction are never saved on-chain
GasUsed: gasUsed,
}
refOperations := newNativeOperationsWithGas(gasFee, senderAccID)
refOperations = append(refOperations, &types.Operation{
OperationIdentifier: &types.OperationIdentifier{Index: 1},
Type: tx.StakingType().String(),
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: fmt.Sprintf("0"),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
})
operations, rosettaError := GetNativeOperationsFromStakingTransaction(tx, receipt)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetBasicTransferOperations(t *testing.T) {
signer := hmytypes.NewEIP155Signer(params.TestChainConfig.ChainID)
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)
}
receiverAccID, rosettaError := newAccountIdentifier(*tx.To())
if rosettaError != nil {
t.Fatal(rosettaError)
}
startingOpID := &types.OperationIdentifier{}
// Test failed 'contract' transaction
refOperations := []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 1,
},
Type: common.NativeTransferOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
},
},
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 2,
},
RelatedOperations: []*types.OperationIdentifier{
{
Index: startingOpID.Index + 1,
},
},
Type: common.NativeTransferOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: receiverAccID,
Amount: &types.Amount{
Value: fmt.Sprintf("%v", tx.Value().Uint64()),
Currency: &common.NativeCurrency,
},
},
}
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusFailed,
}
opIndex := startingOpID.Index + 1
operations, rosettaError := getBasicTransferNativeOperations(tx, receipt, senderAddr, tx.To(), &opIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
// Test successful plain / contract transaction
refOperations[0].Status = common.SuccessOperationStatus.Status
refOperations[1].Status = common.SuccessOperationStatus.Status
receipt.Status = hmytypes.ReceiptStatusSuccessful
operations, rosettaError = getBasicTransferNativeOperations(tx, receipt, senderAddr, tx.To(), &opIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestGetCrossShardSenderTransferNativeOperations(t *testing.T) {
signer := hmytypes.NewEIP155Signer(params.TestChainConfig.ChainID)
tx, err := helpers.CreateTestTransaction(
signer, 0, 1, 0, 1e18, gasPrice, big.NewInt(1), []byte("data-does-nothing"),
)
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)
}
startingOpID := &types.OperationIdentifier{}
receiverAccID, rosettaError := newAccountIdentifier(*tx.To())
if rosettaError != nil {
t.Error(rosettaError)
}
metadata, err := types.MarshalMap(common.CrossShardTransactionOperationMetadata{
From: senderAccID,
To: receiverAccID,
})
if err != nil {
t.Fatal(err)
}
refOperations := []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 1,
},
Type: common.NativeCrossShardTransferOperation,
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
},
Metadata: metadata,
},
}
opIndex := startingOpID.Index + 1
operations, rosettaError := getCrossShardSenderTransferNativeOperations(tx, senderAddr, &opIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
var (
testExecResultForInternalTx = &hmy.ExecutionResult{
StructLogs: []hmy.StructLogRes{
{
Pc: 1316,
Op: "DUP9",
CallerAddress: ethcommon.HexToAddress("0x2a44f609f860d4ff8835f9ec1d9b1acdae1fd9cb"),
ContractAddress: ethcommon.HexToAddress("0x4c4fde977fbbe722cddf5719d7edd488510be16a"),
Gas: 6677398,
GasCost: 3,
Depth: 1,
Stack: []string{
"0000000000000000000000000000000000000000000000000000000023024408",
"000000000000000000000000000000000000000000000000000000000000019a",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000050",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000021e19e0c9bab2400000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"00000000000000000000000000000000000000000000021e19e0c9bab2400000",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
},
Memory: []string{
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000080",
},
Storage: map[string]string{
"43a43725d4b041c11b63ea10be0771546465a0c0654fd13c2712f2a8ce3f8b85": "0000000000000000000000000000000000000000000000000000000000000050",
},
},
{
Pc: 1317,
Op: vm.CALL.String(),
CallerAddress: ethcommon.HexToAddress("0x2a44f609f860d4ff8835f9ec1d9b1acdae1fd9cb"),
ContractAddress: ethcommon.HexToAddress("0x4c4fde977fbbe722cddf5719d7edd488510be16a"),
Gas: 6677395,
GasCost: 9700,
Depth: 1,
Stack: []string{
"0000000000000000000000000000000000000000000000000000000023024408",
"000000000000000000000000000000000000000000000000000000000000019a",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000050",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000021e19e0c9bab2400000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000002710",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
},
Memory: []string{
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000080",
},
Storage: map[string]string{
"43a43725d4b041c11b63ea10be0771546465a0c0654fd13c2712f2a8ce3f8b85": "0000000000000000000000000000000000000000000000000000000000000050",
},
},
{
Pc: 1318,
Op: "SWAP4",
CallerAddress: ethcommon.HexToAddress("0x2a44f609f860d4ff8835f9ec1d9b1acdae1fd9cb"),
ContractAddress: ethcommon.HexToAddress("0x4c4fde977fbbe722cddf5719d7edd488510be16a"),
Gas: 6669995,
GasCost: 3,
Depth: 1,
Stack: []string{
"0000000000000000000000000000000000000000000000000000000023024408",
"000000000000000000000000000000000000000000000000000000000000019a",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000050",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000021e19e0c9bab2400000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000001",
},
Memory: []string{
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000080",
},
Storage: map[string]string{
"43a43725d4b041c11b63ea10be0771546465a0c0654fd13c2712f2a8ce3f8b85": "0000000000000000000000000000000000000000000000000000000000000050",
},
},
{
Pc: 1319,
Op: vm.CALLCODE.String(),
CallerAddress: ethcommon.HexToAddress("0x2a44f609f860d4ff8835f9ec1d9b1acdae1fd9cb"),
ContractAddress: ethcommon.HexToAddress("0x4c4fde977fbbe722cddf5719d7edd488510be16a"),
Gas: 6677395,
GasCost: 9700,
Depth: 1,
Stack: []string{
"0000000000000000000000000000000000000000000000000000000023024408",
"000000000000000000000000000000000000000000000000000000000000019a",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000050",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000021e19e0c9bab2400000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000002710",
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000000",
},
Memory: []string{
"0000000000000000000000007c41e0668b551f4f902cfaec05b5bdca68b124ce",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000080",
},
Storage: map[string]string{
"43a43725d4b041c11b63ea10be0771546465a0c0654fd13c2712f2a8ce3f8b85": "0000000000000000000000000000000000000000000000000000000000000050",
},
},
},
}
testExecResultForInternalTxValueSum = uint64(20000)
)
func TestGetContractInternalTransferNativeOperations(t *testing.T) {
refStatus := common.SuccessOperationStatus.Status
refStartingIndex := int64(23)
baseValidation := func(ops []*types.Operation, expectedValueSum uint64) {
prevIndex := int64(-1)
valueSum := int64(0)
absValueSum := uint64(0)
for i, op := range ops {
if op.OperationIdentifier.Index <= prevIndex {
t.Errorf("expect prev index (%v) < curr index (%v) for op %v",
prevIndex, op.OperationIdentifier.Index, i,
)
}
prevIndex = op.OperationIdentifier.Index
if op.Status != refStatus {
t.Errorf("wrong status for op %v", i)
}
if op.Type != common.NativeTransferOperation {
t.Errorf("wrong operation type for op %v", i)
}
if types.Hash(op.Amount.Currency) != common.NativeCurrencyHash {
t.Errorf("wrong currency for op %v", i)
}
val, err := types.AmountValue(op.Amount)
if err != nil {
t.Error(err)
}
valueSum += val.Int64()
absValueSum += val.Abs(val).Uint64()
}
if valueSum != 0 {
t.Errorf("expected sum of all non-gas values to be 0")
}
if expectedValueSum*2 != absValueSum {
t.Errorf("sum of all positive values of operations do not match execpted sum of values")
}
}
testOps, rosettaError := getContractInternalTransferNativeOperations(
testExecResultForInternalTx, refStatus, &refStartingIndex,
)
if rosettaError != nil {
t.Error(rosettaError)
}
baseValidation(testOps, testExecResultForInternalTxValueSum)
if len(testOps) == 0 {
t.Errorf("expect atleast 1 operation")
}
if testOps[0].OperationIdentifier.Index != refStartingIndex {
t.Errorf("expected starting index to be %v", refStartingIndex)
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
testOps, rosettaError = getContractInternalTransferNativeOperations(
testExecResultForInternalTx, refStatus, nil,
)
if rosettaError != nil {
t.Error(rosettaError)
}
baseValidation(testOps, testExecResultForInternalTxValueSum)
if len(testOps) == 0 {
t.Errorf("expect atleast 1 operation")
}
if testOps[0].OperationIdentifier.Index != 0 {
t.Errorf("expected starting index to be 0")
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
testOps, rosettaError = getContractInternalTransferNativeOperations(
nil, refStatus, nil,
)
if rosettaError != nil {
t.Error(rosettaError)
}
if len(testOps) != 0 {
t.Errorf("expected len 0 test operations for nil execution result")
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
testOps, rosettaError = getContractInternalTransferNativeOperations(
&hmy.ExecutionResult{}, refStatus, nil,
)
if rosettaError != nil {
t.Error(rosettaError)
}
if len(testOps) != 0 {
t.Errorf("expected len 0 test operations for nil struct logs")
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
}
func TestGetContractTransferNativeOperations(t *testing.T) {
signer := hmytypes.NewEIP155Signer(params.TestChainConfig.ChainID)
refTxValue := big.NewInt(1)
refTx, err := helpers.CreateTestTransaction(
signer, 0, 0, 0, 1e18, gasPrice, refTxValue, []byte("blah-blah-blah"),
)
if err != nil {
t.Fatal(err.Error())
}
refSenderAddr, err := refTx.SenderAddress()
if err != nil {
t.Fatal(err.Error())
}
refStatus := common.SuccessOperationStatus.Status
refStartingIndex := int64(23)
refReceipt := &hmytypes.Receipt{
PostState: nil,
Status: 1,
GasUsed: params.TxGas * 3, // somme arb number > TxGas
}
baseValidation := func(ops []*types.Operation, expectedValueSum uint64) {
prevIndex := int64(-1)
valueSum := int64(0)
absValueSum := uint64(0)
for i, op := range ops {
if op.OperationIdentifier.Index <= prevIndex {
t.Errorf("expect prev index (%v) < curr index (%v) for op %v",
prevIndex, op.OperationIdentifier.Index, i,
)
}
prevIndex = op.OperationIdentifier.Index
if op.Status != refStatus {
t.Errorf("wrong status for op %v", i)
}
if types.Hash(op.Amount.Currency) != common.NativeCurrencyHash {
t.Errorf("wrong currency for op %v", i)
}
if op.Type == common.ExpendGasOperation {
continue
}
if op.Type != common.NativeTransferOperation {
t.Errorf("wrong operation type for op %v", i)
}
val, err := types.AmountValue(op.Amount)
if err != nil {
t.Error(err)
}
valueSum += val.Int64()
absValueSum += val.Abs(val).Uint64()
}
if valueSum != 0 {
t.Errorf("expected sum of all non-gas values to be 0")
}
if expectedValueSum*2 != absValueSum {
t.Errorf("sum of all positive values of operations do not match execpted sum of values")
}
}
testOps, rosettaError := getContractTransferNativeOperations(
refTx, refReceipt, refSenderAddr, refTx.To(),
&ContractInfo{ExecutionResult: testExecResultForInternalTx}, &refStartingIndex,
)
if rosettaError != nil {
t.Error(rosettaError)
}
baseValidation(testOps, testExecResultForInternalTxValueSum+refTxValue.Uint64())
if len(testOps) == 0 {
t.Errorf("expect atleast 1 operation")
}
if testOps[0].OperationIdentifier.Index != refStartingIndex {
t.Errorf("expected starting index to be %v", refStartingIndex)
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
testOps, rosettaError = getContractTransferNativeOperations(
refTx, refReceipt, refSenderAddr, refTx.To(),
&ContractInfo{ExecutionResult: testExecResultForInternalTx}, nil,
)
if rosettaError != nil {
t.Error(rosettaError)
}
baseValidation(testOps, testExecResultForInternalTxValueSum+refTxValue.Uint64())
if len(testOps) == 0 {
t.Errorf("expect atleast 1 operation")
}
if testOps[0].OperationIdentifier.Index != 0 {
t.Errorf("expected starting index to be 0")
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
testOps, rosettaError = getContractTransferNativeOperations(
refTx, refReceipt, refSenderAddr, refTx.To(),
&ContractInfo{}, nil,
)
if rosettaError != nil {
t.Error(rosettaError)
}
baseValidation(testOps, refTxValue.Uint64())
if len(testOps) == 0 {
t.Errorf("expect atleast 1 operation")
}
if testOps[0].OperationIdentifier.Index != 0 {
t.Errorf("expected starting index to be 0")
}
if len(testOps) > 3 {
t.Errorf("expect at most 3 operations for nil ExecutionResult")
}
if err := assertNativeOperationTypeUniquenessInvariant(testOps); err != nil {
t.Error(err)
}
}
func TestGetContractCreationNativeOperations(t *testing.T) {
dummyContractKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf(err.Error())
}
chainID := params.TestChainConfig.ChainID
signer := hmytypes.NewEIP155Signer(chainID)
tx, err := helpers.CreateTestContractCreationTransaction(
signer, 0, 0, 1e18, gasPrice, big.NewInt(0), []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)
}
startingOpID := &types.OperationIdentifier{}
// Test failed contract creation
contractAddr := crypto.PubkeyToAddress(dummyContractKey.PublicKey)
contractAddressID, rosettaError := newAccountIdentifier(contractAddr)
if rosettaError != nil {
t.Fatal(rosettaError)
}
refOperations := []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 1,
},
Type: common.ContractCreationOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
},
},
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 2,
},
RelatedOperations: []*types.OperationIdentifier{
{
Index: startingOpID.Index + 1,
},
},
Type: common.ContractCreationOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: contractAddressID,
Amount: &types.Amount{
Value: tx.Value().String(),
Currency: &common.NativeCurrency,
},
},
}
receipt := &hmytypes.Receipt{
Status: hmytypes.ReceiptStatusFailed,
ContractAddress: contractAddr,
}
opIndex := startingOpID.Index + 1
operations, rosettaError := getContractCreationNativeOperations(tx, receipt, senderAddr, &ContractInfo{}, &opIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
// Test successful contract creation
refOperations[0].Status = common.SuccessOperationStatus.Status
refOperations[1].Status = common.SuccessOperationStatus.Status
receipt.Status = hmytypes.ReceiptStatusSuccessful // Indicate successful tx
operations, rosettaError = getContractCreationNativeOperations(tx, receipt, senderAddr, &ContractInfo{}, &opIndex)
if rosettaError != nil {
t.Fatal(rosettaError)
}
if !reflect.DeepEqual(operations, refOperations) {
t.Errorf("Expected operations to be %v not %v", refOperations, operations)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
traceValidation := func(ops []*types.Operation, expectedValueSum uint64) {
prevIndex := int64(-1)
valueSum := int64(0)
absValueSum := uint64(0)
for i, op := range ops {
if op.OperationIdentifier.Index <= prevIndex {
t.Errorf("expect prev index (%v) < curr index (%v) for op %v",
prevIndex, op.OperationIdentifier.Index, i,
)
}
prevIndex = op.OperationIdentifier.Index
if op.Status != refOperations[0].Status {
t.Errorf("wrong status for op %v", i)
}
if types.Hash(op.Amount.Currency) != common.NativeCurrencyHash {
t.Errorf("wrong currency for op %v", i)
}
if op.Type == common.ExpendGasOperation || op.Type == common.ContractCreationOperation {
continue
}
if op.Type != common.NativeTransferOperation {
t.Errorf("wrong operation type for op %v", i)
}
val, err := types.AmountValue(op.Amount)
if err != nil {
t.Error(err)
}
valueSum += val.Int64()
absValueSum += val.Abs(val).Uint64()
}
if valueSum != 0 {
t.Errorf("expected sum of all non-gas values to be 0")
}
if expectedValueSum*2 != absValueSum {
t.Errorf("sum of all positive values of operations do not match execpted sum of values")
}
}
operations, rosettaError = getContractCreationNativeOperations(
tx, receipt, senderAddr, &ContractInfo{ExecutionResult: testExecResultForInternalTx}, &opIndex,
)
if rosettaError != nil {
t.Fatal(rosettaError)
}
traceValidation(operations, testExecResultForInternalTxValueSum)
if len(operations) == 0 {
t.Errorf("expect atleast 1 operation")
}
if operations[0].OperationIdentifier.Index != opIndex {
t.Errorf("expect first operation to be %v", opIndex)
}
if err := assertNativeOperationTypeUniquenessInvariant(operations); err != nil {
t.Error(err)
}
}
func TestNewNativeOperations(t *testing.T) {
accountID := &types.AccountIdentifier{
Address: "test-address",
}
gasFee := big.NewInt(int64(1e18))
amount := &types.Amount{
Value: negativeBigValue(gasFee),
Currency: &common.NativeCurrency,
}
ops := newNativeOperationsWithGas(gasFee, accountID)
if len(ops) != 1 {
t.Fatalf("Expected new operations to be of length 1")
}
if !reflect.DeepEqual(ops[0].Account, accountID) {
t.Errorf("Expected account ID to be %v not %v", accountID, ops[0].OperationIdentifier)
}
if !reflect.DeepEqual(ops[0].Amount, amount) {
t.Errorf("Expected amount to be %v not %v", amount, ops[0].Amount)
}
if ops[0].Type != common.ExpendGasOperation {
t.Errorf("Expected operation to be %v not %v", common.ExpendGasOperation, ops[0].Type)
}
if ops[0].OperationIdentifier.Index != 0 {
t.Errorf("Expected operational ID to be of index 0")
}
if ops[0].Status != common.SuccessOperationStatus.Status {
t.Errorf("Expected operation status to be %v", common.SuccessOperationStatus.Status)
}
}