decred.org/dcrdex@v1.0.5/dex/networks/eth/txdata.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package eth
     5  
     6  import (
     7  	"fmt"
     8  	"math/big"
     9  	"strings"
    10  	"time"
    11  
    12  	swapv0 "decred.org/dcrdex/dex/networks/eth/contracts/v0"
    13  	"github.com/ethereum/go-ethereum/accounts/abi"
    14  	"github.com/ethereum/go-ethereum/common"
    15  )
    16  
    17  // ParseInitiateData parses the calldata used to call the initiate function of a
    18  // specific version of the swap contract. It returns the the list of initiations
    19  // done in the call and errors if the call data does not call initiate initiate
    20  // with expected argument types.
    21  func ParseInitiateData(calldata []byte, contractVersion uint32) (map[[SecretHashSize]byte]*Initiation, error) {
    22  	txDataHandler, ok := txDataHandlers[contractVersion]
    23  	if !ok {
    24  		return nil, fmt.Errorf("contract version %v does not exist", contractVersion)
    25  	}
    26  
    27  	return txDataHandler.parseInitiateData(calldata)
    28  }
    29  
    30  // ParseRedeemData parses the calldata used to call the redeem function of a
    31  // specific version of the swap contract. It returns the the list of redemptions
    32  // done in the call and errors if the call data does not call redeem with expected
    33  // argument types.
    34  func ParseRedeemData(calldata []byte, contractVersion uint32) (map[[SecretHashSize]byte]*Redemption, error) {
    35  	txDataHandler, ok := txDataHandlers[contractVersion]
    36  	if !ok {
    37  		return nil, fmt.Errorf("contract version %v does not exist", contractVersion)
    38  	}
    39  
    40  	return txDataHandler.parseRedeemData(calldata)
    41  }
    42  
    43  // ParseRefundData parses the calldata used to call the refund function of a
    44  // specific version of the swap contract. It returns the secret hash and errors
    45  // if the call data does not call refund with expected argument types.
    46  func ParseRefundData(calldata []byte, contractVersion uint32) ([SecretHashSize]byte, error) {
    47  	txDataHandler, ok := txDataHandlers[contractVersion]
    48  	if !ok {
    49  		return [32]byte{}, fmt.Errorf("contract version %v does not exist", contractVersion)
    50  	}
    51  
    52  	return txDataHandler.parseRefundData(calldata)
    53  }
    54  
    55  // ABIs maps each swap contract's version to that version's parsed ABI.
    56  var ABIs = initAbis()
    57  
    58  func initAbis() map[uint32]*abi.ABI {
    59  	v0ABI, err := abi.JSON(strings.NewReader(swapv0.ETHSwapABI))
    60  	if err != nil {
    61  		panic(fmt.Sprintf("failed to parse abi: %v", err))
    62  	}
    63  
    64  	return map[uint32]*abi.ABI{
    65  		0: &v0ABI,
    66  	}
    67  }
    68  
    69  type txDataHandler interface {
    70  	parseInitiateData([]byte) (map[[SecretHashSize]byte]*Initiation, error)
    71  	parseRedeemData([]byte) (map[[SecretHashSize]byte]*Redemption, error)
    72  	parseRefundData([]byte) ([32]byte, error)
    73  }
    74  
    75  var txDataHandlers = map[uint32]txDataHandler{
    76  	0: newTxDataV0(),
    77  }
    78  
    79  type txDataHandlerV0 struct {
    80  	initiateFuncName string
    81  	redeemFuncName   string
    82  	refundFuncName   string
    83  }
    84  
    85  func newTxDataV0() *txDataHandlerV0 {
    86  	return &txDataHandlerV0{
    87  		initiateFuncName: "initiate",
    88  		redeemFuncName:   "redeem",
    89  		refundFuncName:   "refund",
    90  	}
    91  }
    92  
    93  func (t *txDataHandlerV0) parseInitiateData(calldata []byte) (map[[SecretHashSize]byte]*Initiation, error) {
    94  	decoded, err := ParseCallData(calldata, ABIs[0])
    95  	if err != nil {
    96  		return nil, fmt.Errorf("unable to parse call data: %v", err)
    97  	}
    98  	if decoded.Name != t.initiateFuncName {
    99  		return nil, fmt.Errorf("expected %v function but got %v", t.initiateFuncName, decoded.Name)
   100  	}
   101  	args := decoded.inputs
   102  	// Any difference in number of args and types than what we expect
   103  	// should be caught by parseCallData, but checking again anyway.
   104  	//
   105  	// TODO: If any of the checks prove redundant, remove them.
   106  	const numArgs = 1
   107  	if len(args) != numArgs {
   108  		return nil, fmt.Errorf("expected %v input args but got %v", numArgs, len(args))
   109  	}
   110  	initiations, ok := args[0].value.([]struct {
   111  		RefundTimestamp *big.Int       `json:"refundTimestamp"`
   112  		SecretHash      [32]byte       `json:"secretHash"`
   113  		Participant     common.Address `json:"participant"`
   114  		Value           *big.Int       `json:"value"`
   115  	})
   116  	if !ok {
   117  		return nil, fmt.Errorf("expected first arg of type []swapv0.ETHSwapInitiation but got %T", args[0].value)
   118  	}
   119  
   120  	// This is done for the compiler to ensure that the type defined above and
   121  	// swapv0.ETHSwapInitiation are the same, other than the tags.
   122  	if len(initiations) > 0 {
   123  		_ = swapv0.ETHSwapInitiation(initiations[0])
   124  	}
   125  
   126  	toReturn := make(map[[SecretHashSize]byte]*Initiation, len(initiations))
   127  	for _, init := range initiations {
   128  		toReturn[init.SecretHash] = &Initiation{
   129  			LockTime:    time.Unix(init.RefundTimestamp.Int64(), 0),
   130  			SecretHash:  init.SecretHash,
   131  			Participant: init.Participant,
   132  			Value:       init.Value,
   133  		}
   134  	}
   135  
   136  	return toReturn, nil
   137  }
   138  
   139  func (t *txDataHandlerV0) parseRedeemData(calldata []byte) (map[[SecretHashSize]byte]*Redemption, error) {
   140  	decoded, err := ParseCallData(calldata, ABIs[0])
   141  	if err != nil {
   142  		return nil, fmt.Errorf("unable to parse call data: %v", err)
   143  	}
   144  	if decoded.Name != t.redeemFuncName {
   145  		return nil, fmt.Errorf("expected %v function but got %v", t.redeemFuncName, decoded.Name)
   146  	}
   147  	args := decoded.inputs
   148  	// Any difference in number of args and types than what we expect
   149  	// should be caught by parseCallData, but checking again anyway.
   150  	//
   151  	// TODO: If any of the checks prove redundant, remove them.
   152  	const numArgs = 1
   153  	if len(args) != numArgs {
   154  		return nil, fmt.Errorf("expected %v redeem args but got %v", numArgs, len(args))
   155  	}
   156  	redemptions, ok := args[0].value.([]struct {
   157  		Secret     [32]byte `json:"secret"`
   158  		SecretHash [32]byte `json:"secretHash"`
   159  	})
   160  	if !ok {
   161  		return nil, fmt.Errorf("expected first arg of type []swapv0.ETHSwapRedemption but got %T", args[0].value)
   162  	}
   163  
   164  	// This is done for the compiler to ensure that the type defined above and
   165  	// swapv0.ETHSwapRedemption are the same, other than the tags.
   166  	if len(redemptions) > 0 {
   167  		_ = swapv0.ETHSwapRedemption(redemptions[0])
   168  	}
   169  
   170  	toReturn := make(map[[SecretHashSize]byte]*Redemption, len(redemptions))
   171  	for _, redemption := range redemptions {
   172  		toReturn[redemption.SecretHash] = &Redemption{
   173  			SecretHash: redemption.SecretHash,
   174  			Secret:     redemption.Secret,
   175  		}
   176  	}
   177  
   178  	return toReturn, nil
   179  }
   180  
   181  func (t *txDataHandlerV0) parseRefundData(calldata []byte) ([32]byte, error) {
   182  	var secretHash [32]byte
   183  
   184  	decoded, err := ParseCallData(calldata, ABIs[0])
   185  	if err != nil {
   186  		return secretHash, fmt.Errorf("unable to parse call data: %v", err)
   187  	}
   188  	if decoded.Name != t.refundFuncName {
   189  		return secretHash, fmt.Errorf("expected %v function but got %v", t.refundFuncName, decoded.Name)
   190  	}
   191  	args := decoded.inputs
   192  	// Any difference in number of args and types than what we expect
   193  	// should be caught by parseCallData, but checking again anyway.
   194  	//
   195  	// TODO: If any of the checks prove redundant, remove them.
   196  	const numArgs = 1
   197  	if len(args) != numArgs {
   198  		return secretHash, fmt.Errorf("expected %v redeem args but got %v", numArgs, len(args))
   199  	}
   200  	secretHash, ok := args[0].value.([32]byte)
   201  	if !ok {
   202  		return secretHash, fmt.Errorf("expected first arg of type [32]byte but got %T", args[0].value)
   203  	}
   204  
   205  	return secretHash, nil
   206  }