Skip to content

Commit

Permalink
Fix withdraw e2e localtest (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
swift1337 authored Feb 21, 2025
1 parent 0245ad3 commit 39742e2
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 60 deletions.
58 changes: 12 additions & 46 deletions localtest/signer/signer_secp256k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"log"
"fmt"
"math/big"

"github.com/block-vision/sui-go-sdk/models"
Expand Down Expand Up @@ -83,6 +83,8 @@ func (s *SignerSecp256k1) GetPublicKey() []byte {
return append([]byte{prefix}, paddedX...)
}

// Address derives SUI address based on ECDSA public key
// See https://docs.sui.io/concepts/cryptography/transaction-auth/signatures
func (s *SignerSecp256k1) Address() string {
// Get the public key bytes
pubKeyBytes := s.GetPublicKey()
Expand All @@ -107,46 +109,6 @@ func (s *SignerSecp256k1) Address() string {
return "0x" + hex.EncodeToString(addrBytes)
}

type SignedMessageSerializedSig struct {
Message string `json:"message"`
Signature string `json:"signature"`
}

// // https://docs.sui.io/concepts/cryptography/transaction-auth/signatures
// func (s *SignerSecp256k1) SignMessage(data string, scope constant.IntentScope) (*SignedMessageSerializedSig, error) {
// txBytes, _ := base64.StdEncoding.DecodeString(data)
// message := models.NewMessageWithIntent(txBytes, scope)
// digest := blake2b.Sum256(message)
// var noHash crypto.Hash
// sigBytes, err := s.privkey.Sign(rand.Reader, digest[:], noHash)
// if err != nil {
// return nil, err
// }

// ret := &SignedMessageSerializedSig{
// Message: data,
// Signature: ToSerializedSignature(sigBytes, s.GetPublicKey()),
// }
// return ret, nil
// }

// func (s *SignerSecp256k1) SignTransaction(b64TxBytes string) (*models.SignedTransactionSerializedSig, error) {
// result, err := s.SignMessage(b64TxBytes, constant.PersonalMessageIntentScope)
// if err != nil {
// return nil, err
// }

// return &models.SignedTransactionSerializedSig{
// TxBytes: result.Message,
// Signature: result.Signature,
// }, nil
// }

// func (s *SignerSecp256k1) SignPersonalMessage(message string) (*SignedMessageSerializedSig, error) {
// b64Message := base64.StdEncoding.EncodeToString([]byte(message))
// return s.SignMessage(b64Message, constant.PersonalMessageIntentScope)
// }

func ToSerializedSignature(signature, pubKey []byte) string {
signatureLen := len(signature)
pubKeyLen := len(pubKey)
Expand All @@ -157,9 +119,13 @@ func ToSerializedSignature(signature, pubKey []byte) string {
return base64.StdEncoding.EncodeToString(serializedSignature)
}

// SignAndExecuteTransactionBlock sign a transaction block and submit to the Fullnode for execution.
// adapted from sui-go-sdk/sui/signer.go for secp256k1
func (s *SignerSecp256k1) SignAndExecuteTransactionBlock(ctx context.Context, cli sui.ISuiAPI, req models.SignAndExecuteTransactionBlockRequest) (models.SuiTransactionBlockResponse, error) {
// SignAndExecuteTransactionBlock signs a tx block and submits it to RPC.
// Adapted from sui-go-sdk/sui/signer.go for secp256k1
func (s *SignerSecp256k1) SignAndExecuteTransactionBlock(
ctx context.Context,
cli sui.ISuiAPI,
req models.SignAndExecuteTransactionBlockRequest,
) (models.SuiTransactionBlockResponse, error) {
txBytes, _ := base64.StdEncoding.DecodeString(req.TxnMetaData.TxBytes)
message := messageWithIntent(txBytes)
digest1 := blake2b.Sum256(message)
Expand All @@ -169,9 +135,9 @@ func (s *SignerSecp256k1) SignAndExecuteTransactionBlock(ctx context.Context, cl
// privBytes := crypto.FromECDSA(s.privkey)
sigBytes, err := crypto2.Sign(digest2[:], s.privkey)
if err != nil {
log.Printf("SignAndExecuteTransactionBlock: %v", err)
return models.SuiTransactionBlockResponse{}, err
return models.SuiTransactionBlockResponse{}, fmt.Errorf("failed to sign tx: %w", err)
}

sigBytes = sigBytes[:64]

signature := ToSerializedSignature(sigBytes, s.GetPublicKey())
Expand Down
66 changes: 52 additions & 14 deletions localtest/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import (

func TestDeposit(ts *TestSuite) {
// ARRANGE
// Request some SUI from the faucet
ts.RequestLocalNetSuiFromFaucet(string(ts.TSS.Address()))

// Get TSS coin object id
coinObjectId, err := filterOwnedObject(ts.Client, ts.TSS.Address(), "0x2::coin::Coin<0x2::sui::SUI>")
require.NoError(ts, err)

// Given deposit tx
zetaEthAddress := "0x7c125C1d515b8945841b3d5144a060115C58725F"
tx, err := ts.Client.MoveCall(ts.Ctx, models.MoveCallRequest{
Signer: ts.TSS.Address(),
Expand All @@ -27,9 +30,11 @@ func TestDeposit(ts *TestSuite) {
})
require.NoError(ts, err)

// ACT
// Deposit to the gateway
resp, err := ts.TSS.SignAndExecuteTransactionBlock(ts.Ctx, ts.Client, models.SignAndExecuteTransactionBlockRequest{
// not used; the TSS' own private scep256k1 key is used
PriKey: ts.Signer.PriKey,
// not used; the TSS' own private ecdsa key is used
PriKey: nil,
TxnMetaData: tx,
Options: models.SuiTransactionBlockOptions{
ShowEffects: true,
Expand All @@ -38,53 +43,84 @@ func TestDeposit(ts *TestSuite) {
},
RequestType: "WaitForLocalExecution",
})

// ASSERT
require.NoError(ts, err)
require.Equal(ts, "success", resp.Effects.Status.Status)

// Check amount
amtStr := resp.Events[0].ParsedJson["amount"].(string)
ts.Log("Deposit amount: %s", amtStr)

amount, err := strconv.Atoi(amtStr)
require.NoError(ts, err)
require.NotEmpty(ts, amount)

// Check receiver
receiverAddrHex := resp.Events[0].ParsedJson["receiver"].(string)

require.Equal(ts, zetaEthAddress, receiverAddrHex)

ts.Log("Event match! receiver address: %s", receiverAddrHex)
}

func TestWithdrawal(ts *TestSuite) {
// acquire the WithdrawCap object first
typeName := fmt.Sprintf("%s::gateway::WithdrawCap", ts.PackageID)
withdrawCapId, err := filterOwnedObject(ts.Client, ts.Signer.Address, typeName)
// ARRANGE
// Given "withdraw capability" tx
withdrawCapType := fmt.Sprintf("%s::gateway::WithdrawCap", ts.PackageID)
withdrawCapID, err := filterOwnedObject(ts.Client, ts.Signer.Address, withdrawCapType)
require.NoError(ts, err)

ts.Log("WithdrawCap object id %s", withdrawCapID)
require.NotEmpty(ts, withdrawCapID)

// Note that the Gateway was deployed by SUI wallet (ts.Signer);
// We want to transfer its ownership to TSS to mimic the real behavior.
ts.Log("Transfer ownership of WithdrawCap to TSS")

// Given withdrawCap ownership transfer tx from Signer to TSS
transferTx, err := ts.Client.MoveCall(ts.Ctx, models.MoveCallRequest{
Signer: ts.Signer.Address,
PackageObjectId: "0x2",
Module: "transfer",
Function: "public_transfer",
TypeArguments: []any{withdrawCapType},
Arguments: []any{withdrawCapID, ts.TSS.Address()},
GasBudget: "5000000000",
})
require.NoError(ts, err)

ts.Log("WithdrawCap id %s", withdrawCapId)
require.NotEmpty(ts, withdrawCapId)
// Execute the transfer of withdrawCap ownership
resp, err := ts.Client.SignAndExecuteTransactionBlock(ts.Ctx, models.SignAndExecuteTransactionBlockRequest{
PriKey: ts.Signer.PriKey,
TxnMetaData: transferTx,
Options: models.SuiTransactionBlockOptions{ShowEffects: true},
RequestType: "WaitForLocalExecution",
})

require.NoError(ts, err)
require.Equal(ts, "success", resp.Effects.Status.Status, "failed %+v", resp.Effects.Status)

// Given withdraw tx
var (
bob = "0x12030d7d9a343d7c31856da0bf6c5706b34035a610284ff5a47e11e990ce4c5b"
amt = "12345"
nonce = "0"
)

tx, err := ts.Client.MoveCall(ts.Ctx, models.MoveCallRequest{
Signer: ts.Signer.Address,
Signer: ts.TSS.Address(),
PackageObjectId: ts.PackageID,
Module: "gateway",
Function: "withdraw",
TypeArguments: []any{"0x2::sui::SUI"},
Arguments: []any{ts.GatewayObjectID, amt, nonce, bob, withdrawCapId},
Arguments: []any{ts.GatewayObjectID, amt, nonce, bob, withdrawCapID},
GasBudget: "5000000000",
})

require.NoError(ts, err)

resp, err := ts.Client.SignAndExecuteTransactionBlock(ts.Ctx, models.SignAndExecuteTransactionBlockRequest{
// ACT
// Withdraw on behalf of TSS
resp, err = ts.TSS.SignAndExecuteTransactionBlock(ts.Ctx, ts.Client, models.SignAndExecuteTransactionBlockRequest{
TxnMetaData: tx,
PriKey: ts.Signer.PriKey,
Options: models.SuiTransactionBlockOptions{
ShowEffects: true,
ShowBalanceChanges: true,
Expand All @@ -93,9 +129,11 @@ func TestWithdrawal(ts *TestSuite) {
RequestType: "WaitForLocalExecution",
})

// ASSERT
require.NoError(ts, err)
require.Equal(ts, "success", resp.Effects.Status.Status)

// Check amount
for _, change := range resp.BalanceChanges {
if change.Owner.AddressOwner == bob {
ts.Log("Withdraw amount: %s", change.Amount)
Expand Down

0 comments on commit 39742e2

Please sign in to comment.