Skip to content

Commit

Permalink
Native Assets: Hedera to EVM (#280)
Browse files Browse the repository at this point in the history
* Whole working flow

Signed-off-by: Georgi Yazovaliyski <[email protected]>
  • Loading branch information
georgiyazovaliiski authored Aug 31, 2021
1 parent 7a84f7e commit 1a77e2b
Show file tree
Hide file tree
Showing 32 changed files with 653 additions and 174 deletions.
23 changes: 23 additions & 0 deletions app/clients/hedera/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ func (hc Node) SubmitScheduledTokenMintTransaction(tokenID hedera.TokenID, amoun
return hc.submitScheduledMintTransaction(payerAccountID, memo, tokenMintTx)
}

// SubmitScheduledTokenBurnTransaction creates a token burn transaction and submits it as a scheduled burn transaction
func (hc Node) SubmitScheduledTokenBurnTransaction(tokenID hedera.TokenID, amount int64, payerAccountID hedera.AccountID, memo string) (*hedera.TransactionResponse, error) {
tokenBurnTx := hedera.NewTokenBurnTransaction().SetTokenID(tokenID).SetAmount(uint64(amount))
return hc.submitScheduledBurnTransaction(payerAccountID, memo, tokenBurnTx)
}

// SubmitTopicConsensusMessage submits the provided message bytes to the
// specified HCS `topicId`
func (hc Node) SubmitTopicConsensusMessage(topicId hedera.TopicID, message []byte) (*hedera.TransactionID, error) {
Expand Down Expand Up @@ -158,6 +164,23 @@ func (hc Node) submitScheduledMintTransaction(payerAccountID hedera.AccountID, m
return hc.submitScheduledTransaction(signedTransaction, payerAccountID, memo)
}

// submitScheduledBurnTransaction freezes the input transaction, signs with operator and submits to HCS
func (hc Node) submitScheduledBurnTransaction(payerAccountID hedera.AccountID, memo string, tx *hedera.TokenBurnTransaction) (*hedera.TransactionResponse, error) {
// TODO: extract 147 to 154 to a different method
tx, err := tx.FreezeWith(hc.GetClient())
if err != nil {
return nil, err
}

signedTransaction, err := tx.
SignWithOperator(hc.GetClient())
if err != nil {
return nil, err
}

return hc.submitScheduledTransaction(signedTransaction, payerAccountID, memo)
}

func (hc Node) submitScheduledTransaction(signedTransaction hedera.ITransaction, payerAccountID hedera.AccountID, memo string) (*hedera.TransactionResponse, error) {
scheduledTx, err := hedera.NewScheduleCreateTransaction().
SetScheduledTransaction(signedTransaction)
Expand Down
7 changes: 7 additions & 0 deletions app/clients/hedera/mirror-node/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ func (c Client) GetAccountTokenMintTransactionsAfterTimestamp(accountId hedera.A
return c.getTransactionsByQuery(transactionsDownloadQuery)
}

func (c Client) GetAccountTokenBurnTransactionsAfterTimestamp(accountId hedera.AccountID, from int64) (*Response, error) {
transactionsDownloadQuery := fmt.Sprintf("?account.id=%s&result=success&timestamp=gt:%s&order=asc&transactiontype=tokenburn",
accountId.String(),
timestampHelper.String(from))
return c.getTransactionsByQuery(transactionsDownloadQuery)
}

func (c Client) GetAccountCreditTransactionsAfterTimestamp(accountId hedera.AccountID, from int64) (*Response, error) {
transactionsDownloadQuery := fmt.Sprintf("?account.id=%s&type=credit&result=success&timestamp=gt:%s&order=asc&transactiontype=cryptotransfer",
accountId.String(),
Expand Down
2 changes: 2 additions & 0 deletions app/domain/client/hedera-node.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ type HederaNode interface {
SubmitScheduleSign(scheduleID hedera.ScheduleID) (*hedera.TransactionResponse, error)
// SubmitScheduledTokenMintTransaction creates a token mint transaction and submits it as a scheduled mint transaction
SubmitScheduledTokenMintTransaction(tokenID hedera.TokenID, amount int64, payerAccountID hedera.AccountID, memo string) (*hedera.TransactionResponse, error)
// SubmitScheduledTokenBurnTransaction creates a token burn transaction and submits it as a scheduled burn transaction
SubmitScheduledTokenBurnTransaction(id hedera.TokenID, amount int64, account hedera.AccountID, memo string) (*hedera.TransactionResponse, error)
}
2 changes: 2 additions & 0 deletions app/domain/client/mirror-node.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
type MirrorNode interface {
// GetAccountTokenMintTransactionsAfterTimestamp queries the hedera mirror node for transactions on a certain account with type TokenMint
GetAccountTokenMintTransactionsAfterTimestamp(accountId hedera.AccountID, from int64) (*mirror_node.Response, error)
// GetAccountTokenBurnTransactionsAfterTimestamp queries the hedera mirror node for transactions on a certain account with type TokenBurn
GetAccountTokenBurnTransactionsAfterTimestamp(accountId hedera.AccountID, from int64) (*mirror_node.Response, error)
GetAccountCreditTransactionsAfterTimestamp(accountId hedera.AccountID, from int64) (*mirror_node.Response, error)
// GetAccountCreditTransactionsBetween returns all incoming Transfers for the specified account between timestamp `from` and `to` excluded
GetAccountCreditTransactionsBetween(accountId hedera.AccountID, from, to int64) ([]mirror_node.Transaction, error)
Expand Down
5 changes: 5 additions & 0 deletions app/domain/repository/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ type Transfer interface {
UpdateStatusSignatureSubmitted(txId string) error
UpdateStatusSignatureMined(txId string) error
UpdateStatusSignatureFailed(txId string) error

// Hedera -> EVM additional statuses
UpdateStatusScheduledTokenBurnSubmitted(txId string) error
UpdateStatusScheduledTokenBurnFailed(txId string) error
UpdateStatusScheduledTokenBurnCompleted(txId string) error
}
2 changes: 2 additions & 0 deletions app/domain/service/scheduled.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ type Scheduled interface {
ExecuteScheduledTransferTransaction(id, asset string, transfers []transfer.Hedera, onExecutionSuccess func(transactionID, scheduleID string), onExecutionFail, onSuccess, onFail func(transactionID string))
// ExecuteScheduledMintTransaction submits a scheduled mint transaction and executes provided functions when necessary
ExecuteScheduledMintTransaction(id, asset string, amount int64, status *chan string, onExecutionSuccess func(transactionID, scheduleID string), onExecutionFail, onSuccess, onFail func(transactionID string))
// ExecuteScheduledBurnTransaction submits a scheduled burn transaction and executes provided functions when necessary
ExecuteScheduledBurnTransaction(id, asset string, amount int64, status *chan string, onExecutionSuccess func(transactionID, scheduleID string), onExecutionFail, onSuccess, onFail func(transactionID string))
}
7 changes: 5 additions & 2 deletions app/domain/service/transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ type Transfers interface {
// InitiateNewTransfer Stores the incoming transfer message into the Database
// aware of already processed transfers
InitiateNewTransfer(tm transfer.Transfer) (*entity.Transfer, error)
// ProcessTransfer processes the transfer message by signing the required
// ProcessNativeTransfer processes the native transfer message by signing the required
// authorisation signature submitting it into the required HCS Topic
ProcessTransfer(tm transfer.Transfer) error
ProcessNativeTransfer(tm transfer.Transfer) error
// ProcessWrappedTransfer processes the wrapped transfer message by signing the required
// authorisation signature submitting it into the required HCS Topic
ProcessWrappedTransfer(tm transfer.Transfer) error
// TransferData returns from the database the given transfer, its signatures and
// calculates if its messages have reached super majority
TransferData(txId string) (TransferData, error)
Expand Down
11 changes: 11 additions & 0 deletions app/helper/sync/statuses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sync

type Statuses string

const (
SCHEDULED_MINT_TYPE = "mint"
SCHEDULED_BURN_TYPE = "burn"
SCHEDULED_TRANSFER_TYPE = "transfer"
DONE = "DONE"
FAIL = "FAIL"
)
45 changes: 36 additions & 9 deletions app/model/transfer/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@

package transfer

type NativeTransfer struct {
Transfer
}

type WrappedTransfer struct {
Transfer
}

// Transfer serves as a model between Transfer Watcher and Handler
type Transfer struct {
TransactionId string
Expand All @@ -24,16 +32,35 @@ type Transfer struct {
NativeAsset string
WrappedAsset string
RouterAddress string
TargetChainID int64
}

// NewNative instantiates Transfer struct ready for submission to the handler
func NewNative(txId, receiver, nativeAsset, wrappedAsset, amount, routerAddress string, targetChainID int64) *NativeTransfer {
return &NativeTransfer{
Transfer: Transfer{
TransactionId: txId,
Receiver: receiver,
Amount: amount,
NativeAsset: nativeAsset,
WrappedAsset: wrappedAsset,
RouterAddress: routerAddress,
TargetChainID: targetChainID,
},
}
}

// New instantiates Transfer struct ready for submission to the handler
func New(txId, receiver, nativeAsset, wrappedAsset, amount, routerAddress string) *Transfer {
return &Transfer{
TransactionId: txId,
Receiver: receiver,
Amount: amount,
NativeAsset: nativeAsset,
WrappedAsset: wrappedAsset,
RouterAddress: routerAddress,
// NewNative instantiates Transfer struct ready for submission to the handler
func NewWrapped(txId, receiver, nativeAsset, wrappedAsset, amount, routerAddress string, targetChainID int64) *WrappedTransfer {
return &WrappedTransfer{
Transfer: Transfer{
TransactionId: txId,
Receiver: receiver,
Amount: amount,
NativeAsset: nativeAsset,
WrappedAsset: wrappedAsset,
RouterAddress: routerAddress,
TargetChainID: targetChainID,
},
}
}
23 changes: 14 additions & 9 deletions app/model/transfer/transfer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,27 @@ const (
nativeAsset = "0.0.123"
wrappedAsset = "0xwrapped00123"
routerAddress = "0xrouteraddress"
chainId = 3
)

func Test_New(t *testing.T) {
expectedTransfer := &Transfer{
TransactionId: txId,
Receiver: receiver,
Amount: amount,
NativeAsset: nativeAsset,
WrappedAsset: wrappedAsset,
RouterAddress: routerAddress,
expectedTransfer := &NativeTransfer{
Transfer: Transfer{
TransactionId: txId,
Receiver: receiver,
Amount: amount,
NativeAsset: nativeAsset,
WrappedAsset: wrappedAsset,
RouterAddress: routerAddress,
TargetChainID: chainId,
},
}
actualTransfer := New(txId,
actualTransfer := NewNative(txId,
receiver,
nativeAsset,
wrappedAsset,
amount,
routerAddress)
routerAddress,
chainId)
assert.Equal(t, expectedTransfer, actualTransfer)
}
1 change: 1 addition & 0 deletions app/persistence/entity/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Transfer struct {
RouterAddress string
Status string
SignatureMsgStatus string
TargetChainID int64
Messages []Message `gorm:"foreignKey:TransferID"`
Fee Fee `gorm:"foreignKey:TransferID"`
}
8 changes: 8 additions & 0 deletions app/persistence/entity/transfer/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,12 @@ const (
// StatusSignatureFailed is a SignatureStatus set if the signature submission TX fails.
// This is a terminal status
StatusSignatureFailed = "SIGNATURE_FAILED"
// StatusScheduledTokenBurnSubmitted is a ScheduledTokenBurn status set if the scheduled token burn TX gets submitted successfully.
StatusScheduledTokenBurnSubmitted = "SCHEDULED_TOKEN_BURN_SUBMITTED"
// StatusScheduledTokenBurnFailed is a ScheduledTokenBurn status set if the scheduled token burn TX fails.
// This is a terminal status
StatusScheduledTokenBurnFailed = "SCHEDULED_TOKEN_BURN_FAILED"
// StatusScheduledTokenBurnCompleted is a ScheduledTokenBurn status set if the scheduled token burn TX completes successfully.
// This is a terminal status
StatusScheduledTokenBurnCompleted = "SCHEDULED_TOKEN_BURN_COMPLETED"
)
15 changes: 14 additions & 1 deletion app/persistence/transfer/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ func (tr Repository) UpdateStatusSignatureFailed(txId string) error {
return tr.updateSignatureStatus(txId, transfer.StatusSignatureFailed)
}

func (tr Repository) UpdateStatusScheduledTokenBurnSubmitted(txId string) error {
return tr.updateSignatureStatus(txId, transfer.StatusScheduledTokenBurnSubmitted)
}

func (tr Repository) UpdateStatusScheduledTokenBurnFailed(txId string) error {
return tr.updateSignatureStatus(txId, transfer.StatusScheduledTokenBurnFailed)
}

func (tr Repository) UpdateStatusScheduledTokenBurnCompleted(txId string) error {
return tr.updateSignatureStatus(txId, transfer.StatusScheduledTokenBurnCompleted)
}

func (tr Repository) create(ct *model.Transfer, status string) (*entity.Transfer, error) {
tx := &entity.Transfer{
TransactionID: ct.TransactionId,
Expand All @@ -154,6 +166,7 @@ func (tr Repository) create(ct *model.Transfer, status string) (*entity.Transfer
NativeAsset: ct.NativeAsset,
WrappedAsset: ct.WrappedAsset,
RouterAddress: ct.RouterAddress,
TargetChainID: ct.TargetChainID,
}
err := tr.dbClient.Create(tx).Error

Expand All @@ -180,7 +193,7 @@ func (tr Repository) updateStatus(txId string, status string) error {
}

func (tr Repository) updateSignatureStatus(txId string, status string) error {
return tr.baseUpdateStatus("signature_msg_status", txId, status, []string{transfer.StatusSignatureSubmitted, transfer.StatusSignatureMined, transfer.StatusSignatureFailed})
return tr.baseUpdateStatus("signature_msg_status", txId, status, []string{transfer.StatusSignatureSubmitted, transfer.StatusSignatureMined, transfer.StatusSignatureFailed, transfer.StatusScheduledTokenBurnCompleted, transfer.StatusScheduledTokenBurnFailed, transfer.StatusScheduledTokenBurnSubmitted})
}

func (tr Repository) baseUpdateStatus(statusColumn, txId, status string, possibleStatuses []string) error {
Expand Down
44 changes: 31 additions & 13 deletions app/process/handler/transfer/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package transfer

import (
"errors"
"github.com/limechain/hedera-eth-bridge-validator/app/domain/service"
model "github.com/limechain/hedera-eth-bridge-validator/app/model/transfer"
"github.com/limechain/hedera-eth-bridge-validator/app/persistence/entity/transfer"
Expand All @@ -38,26 +39,43 @@ func NewHandler(transfersService service.Transfers) *Handler {
}

func (th Handler) Handle(payload interface{}) {
transferMsg, ok := payload.(*model.Transfer)
if !ok {
th.logger.Errorf("Could not cast payload [%s]", payload)
return
var err error
switch t := payload.(type) {
case *model.NativeTransfer:
err = th.initiateTransferAndCheckStatus(t.Transfer)
if err != nil {
return
}

err = th.transfersService.ProcessNativeTransfer(t.Transfer)
if err != nil {
th.logger.Errorf("[%s] - Processing failed. Error: [%s]", t.TransactionId, err)
return
}
case *model.WrappedTransfer:
err = th.initiateTransferAndCheckStatus(t.Transfer)
if err != nil {
return
}

err = th.transfersService.ProcessWrappedTransfer(t.Transfer)
if err != nil {
th.logger.Errorf("[%s] - Processing failed. Error: [%s]", t.TransactionId, err)
return
}
}
}

transactionRecord, err := th.transfersService.InitiateNewTransfer(*transferMsg)
func (th Handler) initiateTransferAndCheckStatus(transferMsg model.Transfer) error {
transactionRecord, err := th.transfersService.InitiateNewTransfer(transferMsg)
if err != nil {
th.logger.Errorf("[%s] - Error occurred while initiating processing. Error: [%s]", transferMsg.TransactionId, err)
return
return err
}

if transactionRecord.Status != transfer.StatusInitial {
th.logger.Debugf("[%s] - Previously added with status [%s]. Skipping further execution.", transactionRecord.TransactionID, transactionRecord.Status)
return
}

err = th.transfersService.ProcessTransfer(*transferMsg)
if err != nil {
th.logger.Errorf("[%s] - Processing failed. Error: [%s]", transferMsg.TransactionId, err)
return
return errors.New("previously-added-record")
}
return nil
}
Loading

0 comments on commit 1a77e2b

Please sign in to comment.