signed_data_test.go 11 KB

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