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.
144 lines
5.7 KiB
144 lines
5.7 KiB
4 years ago
|
package services
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/coinbase/rosetta-sdk-go/types"
|
||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
hmyTypes "github.com/harmony-one/harmony/core/types"
|
||
|
"github.com/harmony-one/harmony/rosetta/common"
|
||
|
)
|
||
|
|
||
|
// ConstructTransaction object (unsigned).
|
||
|
// TODO (dm): implement staking transaction construction
|
||
|
func ConstructTransaction(
|
||
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
||
|
) (response hmyTypes.PoolTransaction, rosettaError *types.Error) {
|
||
|
if components == nil || metadata == nil {
|
||
|
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
|
||
|
"message": "nil components or metadata",
|
||
|
})
|
||
|
}
|
||
|
if metadata.Transaction.FromShardID != nil && *metadata.Transaction.FromShardID != sourceShardID {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": fmt.Sprintf("intended source shard %v != tx metadata source shard %v",
|
||
|
sourceShardID, *metadata.Transaction.FromShardID),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
var tx hmyTypes.PoolTransaction
|
||
|
switch components.Type {
|
||
|
case common.CrossShardTransferOperation:
|
||
|
if tx, rosettaError = constructCrossShardTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
||
|
return nil, rosettaError
|
||
|
}
|
||
|
case common.ContractCreationOperation:
|
||
|
if tx, rosettaError = constructContractCreationTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
||
|
return nil, rosettaError
|
||
|
}
|
||
|
case common.TransferOperation:
|
||
|
if tx, rosettaError = constructPlainTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
||
|
return nil, rosettaError
|
||
|
}
|
||
|
default:
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": fmt.Sprintf("cannot create transaction with component type %v", components.Type),
|
||
|
})
|
||
|
}
|
||
|
return tx, nil
|
||
|
}
|
||
|
|
||
|
// constructCrossShardTransaction ..
|
||
|
func constructCrossShardTransaction(
|
||
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
||
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
||
|
if components.To == nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": "cross shard transfer requires a receiver",
|
||
|
})
|
||
|
}
|
||
|
to, err := getAddress(components.To)
|
||
|
if err != nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": errors.WithMessage(err, "invalid sender address").Error(),
|
||
|
})
|
||
|
}
|
||
|
if metadata.Transaction.FromShardID == nil || metadata.Transaction.ToShardID == nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": "cross shard transfer requires to & from shard IDs",
|
||
|
})
|
||
|
}
|
||
|
if *metadata.Transaction.FromShardID != sourceShardID {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": fmt.Sprintf("cannot send transaction for shard %v", *metadata.Transaction.FromShardID),
|
||
|
})
|
||
|
}
|
||
|
if *metadata.Transaction.FromShardID == *metadata.Transaction.ToShardID {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": "cross-shard transfer cannot be within same shard",
|
||
|
})
|
||
|
}
|
||
|
data := hexutil.Bytes{}
|
||
|
if metadata.Transaction.Data != nil {
|
||
|
if data, err = hexutil.Decode(*metadata.Transaction.Data); err != nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
return hmyTypes.NewCrossShardTransaction(
|
||
|
metadata.Nonce, &to, *metadata.Transaction.FromShardID, *metadata.Transaction.ToShardID,
|
||
|
components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
||
|
), nil
|
||
|
}
|
||
|
|
||
|
// constructContractCreationTransaction ..
|
||
|
func constructContractCreationTransaction(
|
||
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
||
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
||
|
if metadata.Transaction.Data == nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": "contract creation requires data, but none found in transaction metadata",
|
||
|
})
|
||
|
}
|
||
|
data, err := hexutil.Decode(*metadata.Transaction.Data)
|
||
|
if err != nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
||
|
})
|
||
|
}
|
||
|
return hmyTypes.NewContractCreation(
|
||
|
metadata.Nonce, sourceShardID, components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
||
|
), nil
|
||
|
}
|
||
|
|
||
|
// constructPlainTransaction ..
|
||
|
func constructPlainTransaction(
|
||
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
||
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
||
|
if components.To == nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": "cross shard transfer requires a receiver",
|
||
|
})
|
||
|
}
|
||
|
to, err := getAddress(components.To)
|
||
|
if err != nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": errors.WithMessage(err, "invalid sender address").Error(),
|
||
|
})
|
||
|
}
|
||
|
data := hexutil.Bytes{}
|
||
|
if metadata.Transaction.Data != nil {
|
||
|
if data, err = hexutil.Decode(*metadata.Transaction.Data); err != nil {
|
||
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
||
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
return hmyTypes.NewTransaction(
|
||
|
metadata.Nonce, to, sourceShardID, components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
||
|
), nil
|
||
|
}
|