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 }