github.com/bcskill/bcschain/v3@v3.4.9-beta2/signer/core/signed_data_test.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core_test 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "io/ioutil" 24 "path" 25 "strings" 26 "testing" 27 28 "github.com/bcskill/bcschain/v3/accounts/keystore" 29 "github.com/bcskill/bcschain/v3/common" 30 "github.com/bcskill/bcschain/v3/common/hexutil" 31 "github.com/bcskill/bcschain/v3/common/math" 32 "github.com/bcskill/bcschain/v3/crypto" 33 "github.com/bcskill/bcschain/v3/signer/core" 34 ) 35 36 var typesStandard = core.Types{ 37 "EIP712Domain": { 38 { 39 Name: "name", 40 Type: "string", 41 }, 42 { 43 Name: "version", 44 Type: "string", 45 }, 46 { 47 Name: "chainId", 48 Type: "uint256", 49 }, 50 { 51 Name: "verifyingContract", 52 Type: "address", 53 }, 54 }, 55 "Person": { 56 { 57 Name: "name", 58 Type: "string", 59 }, 60 { 61 Name: "wallet", 62 Type: "address", 63 }, 64 }, 65 "Mail": { 66 { 67 Name: "from", 68 Type: "Person", 69 }, 70 { 71 Name: "to", 72 Type: "Person", 73 }, 74 { 75 Name: "contents", 76 Type: "string", 77 }, 78 }, 79 } 80 81 var jsonTypedData = ` 82 { 83 "types": { 84 "EIP712Domain": [ 85 { 86 "name": "name", 87 "type": "string" 88 }, 89 { 90 "name": "version", 91 "type": "string" 92 }, 93 { 94 "name": "chainId", 95 "type": "uint256" 96 }, 97 { 98 "name": "verifyingContract", 99 "type": "address" 100 } 101 ], 102 "Person": [ 103 { 104 "name": "name", 105 "type": "string" 106 }, 107 { 108 "name": "test", 109 "type": "uint8" 110 }, 111 { 112 "name": "wallet", 113 "type": "address" 114 } 115 ], 116 "Mail": [ 117 { 118 "name": "from", 119 "type": "Person" 120 }, 121 { 122 "name": "to", 123 "type": "Person" 124 }, 125 { 126 "name": "contents", 127 "type": "string" 128 } 129 ] 130 }, 131 "primaryType": "Mail", 132 "domain": { 133 "name": "Ether Mail", 134 "version": "1", 135 "chainId": "1", 136 "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" 137 }, 138 "message": { 139 "from": { 140 "name": "Cow", 141 "test": 3, 142 "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" 143 }, 144 "to": { 145 "name": "Bob", 146 "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" 147 }, 148 "contents": "Hello, Bob!" 149 } 150 } 151 ` 152 153 const primaryType = "Mail" 154 155 var domainStandard = core.TypedDataDomain{ 156 "Ether Mail", 157 "1", 158 math.NewHexOrDecimal256(1), 159 "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", 160 "", 161 } 162 163 var messageStandard = map[string]interface{}{ 164 "from": map[string]interface{}{ 165 "name": "Cow", 166 "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", 167 }, 168 "to": map[string]interface{}{ 169 "name": "Bob", 170 "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", 171 }, 172 "contents": "Hello, Bob!", 173 } 174 175 var typedData = core.TypedData{ 176 Types: typesStandard, 177 PrimaryType: primaryType, 178 Domain: domainStandard, 179 Message: messageStandard, 180 } 181 182 func TestSignData(t *testing.T) { 183 api, control := setup(t) 184 //Create two accounts 185 createAccount(control, api, t) 186 createAccount(control, api, t) 187 control.approveCh <- "1" 188 list, err := api.List(context.Background()) 189 if err != nil { 190 t.Fatal(err) 191 } 192 a := common.NewMixedcaseAddress(list[0]) 193 194 control.approveCh <- "Y" 195 control.inputCh <- "wrongpassword" 196 signature, err := api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 197 if signature != nil { 198 t.Errorf("Expected nil-data, got %x", signature) 199 } 200 if err != keystore.ErrDecrypt { 201 t.Errorf("Expected ErrLocked! '%v'", err) 202 } 203 control.approveCh <- "No way" 204 signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 205 if signature != nil { 206 t.Errorf("Expected nil-data, got %x", signature) 207 } 208 if err != core.ErrRequestDenied { 209 t.Errorf("Expected ErrRequestDenied! '%v'", err) 210 } 211 // text/plain 212 control.approveCh <- "Y" 213 control.inputCh <- "a_long_password" 214 signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 215 if err != nil { 216 t.Fatal(err) 217 } 218 if signature == nil || len(signature) != 65 { 219 t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) 220 } 221 // data/typed 222 control.approveCh <- "Y" 223 control.inputCh <- "a_long_password" 224 signature, err = api.SignTypedData(context.Background(), a, typedData) 225 if err != nil { 226 t.Fatal(err) 227 } 228 if signature == nil || len(signature) != 65 { 229 t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) 230 } 231 } 232 233 func TestDomainChainId(t *testing.T) { 234 withoutChainID := core.TypedData{ 235 Types: core.Types{ 236 "EIP712Domain": []core.Type{ 237 {Name: "name", Type: "string"}, 238 }, 239 }, 240 Domain: core.TypedDataDomain{ 241 Name: "test", 242 }, 243 } 244 245 if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { 246 t.Errorf("Expected the chainId key to not be present in the domain map") 247 } 248 // should encode successfully 249 if _, err := withoutChainID.HashStruct("EIP712Domain", withoutChainID.Domain.Map()); err != nil { 250 t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) 251 } 252 withChainID := core.TypedData{ 253 Types: core.Types{ 254 "EIP712Domain": []core.Type{ 255 {Name: "name", Type: "string"}, 256 {Name: "chainId", Type: "uint256"}, 257 }, 258 }, 259 Domain: core.TypedDataDomain{ 260 Name: "test", 261 ChainId: math.NewHexOrDecimal256(1), 262 }, 263 } 264 265 if _, ok := withChainID.Domain.Map()["chainId"]; !ok { 266 t.Errorf("Expected the chainId key be present in the domain map") 267 } 268 // should encode successfully 269 if _, err := withChainID.HashStruct("EIP712Domain", withChainID.Domain.Map()); err != nil { 270 t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) 271 } 272 } 273 274 func TestHashStruct(t *testing.T) { 275 hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 276 if err != nil { 277 t.Fatal(err) 278 } 279 mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 280 if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" { 281 t.Errorf("Expected different hashStruct result (got %s)", mainHash) 282 } 283 284 hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 285 if err != nil { 286 t.Error(err) 287 } 288 domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 289 if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" { 290 t.Errorf("Expected different domain hashStruct result (got %s)", domainHash) 291 } 292 } 293 294 func TestEncodeType(t *testing.T) { 295 domainTypeEncoding := string(typedData.EncodeType("EIP712Domain")) 296 if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" { 297 t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding) 298 } 299 300 mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType)) 301 if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" { 302 t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding) 303 } 304 } 305 306 func TestTypeHash(t *testing.T) { 307 mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType))) 308 if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" { 309 t.Errorf("Expected different typeHash result (got %s)", mailTypeHash) 310 } 311 } 312 313 func TestEncodeData(t *testing.T) { 314 hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) 315 if err != nil { 316 t.Fatal(err) 317 } 318 dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 319 if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" { 320 t.Errorf("Expected different encodeData result (got %s)", dataEncoding) 321 } 322 } 323 324 func TestFormatter(t *testing.T) { 325 var d core.TypedData 326 err := json.Unmarshal([]byte(jsonTypedData), &d) 327 if err != nil { 328 t.Fatalf("unmarshalling failed '%v'", err) 329 } 330 formatted, _ := d.Format() 331 for _, item := range formatted { 332 t.Logf("'%v'\n", item.Pprint(0)) 333 } 334 335 j, _ := json.Marshal(formatted) 336 t.Logf("'%v'\n", string(j)) 337 } 338 339 func sign(typedData core.TypedData) ([]byte, []byte, error) { 340 domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 341 if err != nil { 342 return nil, nil, err 343 } 344 typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 345 if err != nil { 346 return nil, nil, err 347 } 348 rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) 349 sighash := crypto.Keccak256(rawData) 350 return typedDataHash, sighash, nil 351 } 352 353 func TestJsonFiles(t *testing.T) { 354 testfiles, err := ioutil.ReadDir("testdata/") 355 if err != nil { 356 t.Fatalf("failed reading files: %v", err) 357 } 358 for i, fInfo := range testfiles { 359 if !strings.HasSuffix(fInfo.Name(), "json") { 360 continue 361 } 362 expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail") 363 data, err := ioutil.ReadFile(path.Join("testdata", fInfo.Name())) 364 if err != nil { 365 t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) 366 continue 367 } 368 var typedData core.TypedData 369 err = json.Unmarshal(data, &typedData) 370 if err != nil { 371 t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) 372 continue 373 } 374 _, _, err = sign(typedData) 375 t.Logf("Error %v\n", err) 376 if err != nil && !expectedFailure { 377 t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err) 378 } 379 if expectedFailure && err == nil { 380 t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err) 381 } 382 } 383 } 384 385 // TestFuzzerFiles tests some files that have been found by fuzzing to cause 386 // crashes or hangs. 387 func TestFuzzerFiles(t *testing.T) { 388 corpusdir := path.Join("testdata", "fuzzing") 389 testfiles, err := ioutil.ReadDir(corpusdir) 390 if err != nil { 391 t.Fatalf("failed reading files: %v", err) 392 } 393 verbose := false 394 for i, fInfo := range testfiles { 395 data, err := ioutil.ReadFile(path.Join(corpusdir, fInfo.Name())) 396 if err != nil { 397 t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) 398 continue 399 } 400 var typedData core.TypedData 401 err = json.Unmarshal(data, &typedData) 402 if err != nil { 403 t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) 404 continue 405 } 406 _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1) 407 if verbose && err != nil { 408 t.Logf("%d, EncodeData[1] err: %v\n", i, err) 409 } 410 _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1) 411 if verbose && err != nil { 412 t.Logf("%d, EncodeData[2] err: %v\n", i, err) 413 } 414 typedData.Format() 415 } 416 }