topics_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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 abi
  17. import (
  18. "math/big"
  19. "reflect"
  20. "testing"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/crypto"
  23. )
  24. func TestMakeTopics(t *testing.T) {
  25. type args struct {
  26. query [][]interface{}
  27. }
  28. tests := []struct {
  29. name string
  30. args args
  31. want [][]common.Hash
  32. wantErr bool
  33. }{
  34. {
  35. "support fixed byte types, right padded to 32 bytes",
  36. args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
  37. [][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
  38. false,
  39. },
  40. {
  41. "support common hash types in topics",
  42. args{[][]interface{}{{common.Hash{1, 2, 3, 4, 5}}}},
  43. [][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
  44. false,
  45. },
  46. {
  47. "support address types in topics",
  48. args{[][]interface{}{{common.Address{1, 2, 3, 4, 5}}}},
  49. [][]common.Hash{{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5}}},
  50. false,
  51. },
  52. {
  53. "support *big.Int types in topics",
  54. args{[][]interface{}{{big.NewInt(1).Lsh(big.NewInt(2), 254)}}},
  55. [][]common.Hash{{common.Hash{128}}},
  56. false,
  57. },
  58. {
  59. "support boolean types in topics",
  60. args{[][]interface{}{
  61. {true},
  62. {false},
  63. }},
  64. [][]common.Hash{
  65. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
  66. {common.Hash{0}},
  67. },
  68. false,
  69. },
  70. {
  71. "support int/uint(8/16/32/64) types in topics",
  72. args{[][]interface{}{
  73. {int8(-2)},
  74. {int16(-3)},
  75. {int32(-4)},
  76. {int64(-5)},
  77. {int8(1)},
  78. {int16(256)},
  79. {int32(65536)},
  80. {int64(4294967296)},
  81. {uint8(1)},
  82. {uint16(256)},
  83. {uint32(65536)},
  84. {uint64(4294967296)},
  85. }},
  86. [][]common.Hash{
  87. {common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254}},
  88. {common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253}},
  89. {common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252}},
  90. {common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251}},
  91. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
  92. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}},
  93. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}},
  94. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}},
  95. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
  96. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}},
  97. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}},
  98. {common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}},
  99. },
  100. false,
  101. },
  102. {
  103. "support string types in topics",
  104. args{[][]interface{}{{"hello world"}}},
  105. [][]common.Hash{{crypto.Keccak256Hash([]byte("hello world"))}},
  106. false,
  107. },
  108. {
  109. "support byte slice types in topics",
  110. args{[][]interface{}{{[]byte{1, 2, 3}}}},
  111. [][]common.Hash{{crypto.Keccak256Hash([]byte{1, 2, 3})}},
  112. false,
  113. },
  114. }
  115. for _, tt := range tests {
  116. t.Run(tt.name, func(t *testing.T) {
  117. got, err := MakeTopics(tt.args.query...)
  118. if (err != nil) != tt.wantErr {
  119. t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
  120. return
  121. }
  122. if !reflect.DeepEqual(got, tt.want) {
  123. t.Errorf("makeTopics() = %v, want %v", got, tt.want)
  124. }
  125. })
  126. }
  127. }
  128. type args struct {
  129. createObj func() interface{}
  130. resultObj func() interface{}
  131. resultMap func() map[string]interface{}
  132. fields Arguments
  133. topics []common.Hash
  134. }
  135. type bytesStruct struct {
  136. StaticBytes [5]byte
  137. }
  138. type int8Struct struct {
  139. Int8Value int8
  140. }
  141. type int256Struct struct {
  142. Int256Value *big.Int
  143. }
  144. type hashStruct struct {
  145. HashValue common.Hash
  146. }
  147. type funcStruct struct {
  148. FuncValue [24]byte
  149. }
  150. type topicTest struct {
  151. name string
  152. args args
  153. wantErr bool
  154. }
  155. func setupTopicsTests() []topicTest {
  156. bytesType, _ := NewType("bytes5", "", nil)
  157. int8Type, _ := NewType("int8", "", nil)
  158. int256Type, _ := NewType("int256", "", nil)
  159. tupleType, _ := NewType("tuple(int256,int8)", "", nil)
  160. stringType, _ := NewType("string", "", nil)
  161. funcType, _ := NewType("function", "", nil)
  162. tests := []topicTest{
  163. {
  164. name: "support fixed byte types, right padded to 32 bytes",
  165. args: args{
  166. createObj: func() interface{} { return &bytesStruct{} },
  167. resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
  168. resultMap: func() map[string]interface{} {
  169. return map[string]interface{}{"staticBytes": [5]byte{1, 2, 3, 4, 5}}
  170. },
  171. fields: Arguments{Argument{
  172. Name: "staticBytes",
  173. Type: bytesType,
  174. Indexed: true,
  175. }},
  176. topics: []common.Hash{
  177. {1, 2, 3, 4, 5},
  178. },
  179. },
  180. wantErr: false,
  181. },
  182. {
  183. name: "int8 with negative value",
  184. args: args{
  185. createObj: func() interface{} { return &int8Struct{} },
  186. resultObj: func() interface{} { return &int8Struct{Int8Value: -1} },
  187. resultMap: func() map[string]interface{} {
  188. return map[string]interface{}{"int8Value": int8(-1)}
  189. },
  190. fields: Arguments{Argument{
  191. Name: "int8Value",
  192. Type: int8Type,
  193. Indexed: true,
  194. }},
  195. topics: []common.Hash{
  196. {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  197. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
  198. },
  199. },
  200. wantErr: false,
  201. },
  202. {
  203. name: "int256 with negative value",
  204. args: args{
  205. createObj: func() interface{} { return &int256Struct{} },
  206. resultObj: func() interface{} { return &int256Struct{Int256Value: big.NewInt(-1)} },
  207. resultMap: func() map[string]interface{} {
  208. return map[string]interface{}{"int256Value": big.NewInt(-1)}
  209. },
  210. fields: Arguments{Argument{
  211. Name: "int256Value",
  212. Type: int256Type,
  213. Indexed: true,
  214. }},
  215. topics: []common.Hash{
  216. {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  217. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
  218. },
  219. },
  220. wantErr: false,
  221. },
  222. {
  223. name: "hash type",
  224. args: args{
  225. createObj: func() interface{} { return &hashStruct{} },
  226. resultObj: func() interface{} { return &hashStruct{crypto.Keccak256Hash([]byte("stringtopic"))} },
  227. resultMap: func() map[string]interface{} {
  228. return map[string]interface{}{"hashValue": crypto.Keccak256Hash([]byte("stringtopic"))}
  229. },
  230. fields: Arguments{Argument{
  231. Name: "hashValue",
  232. Type: stringType,
  233. Indexed: true,
  234. }},
  235. topics: []common.Hash{
  236. crypto.Keccak256Hash([]byte("stringtopic")),
  237. },
  238. },
  239. wantErr: false,
  240. },
  241. {
  242. name: "function type",
  243. args: args{
  244. createObj: func() interface{} { return &funcStruct{} },
  245. resultObj: func() interface{} {
  246. return &funcStruct{[24]byte{255, 255, 255, 255, 255, 255, 255, 255,
  247. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}
  248. },
  249. resultMap: func() map[string]interface{} {
  250. return map[string]interface{}{"funcValue": [24]byte{255, 255, 255, 255, 255, 255, 255, 255,
  251. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}
  252. },
  253. fields: Arguments{Argument{
  254. Name: "funcValue",
  255. Type: funcType,
  256. Indexed: true,
  257. }},
  258. topics: []common.Hash{
  259. {0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
  260. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
  261. },
  262. },
  263. wantErr: false,
  264. },
  265. {
  266. name: "error on topic/field count mismatch",
  267. args: args{
  268. createObj: func() interface{} { return nil },
  269. resultObj: func() interface{} { return nil },
  270. resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
  271. fields: Arguments{Argument{
  272. Name: "tupletype",
  273. Type: tupleType,
  274. Indexed: true,
  275. }},
  276. topics: []common.Hash{},
  277. },
  278. wantErr: true,
  279. },
  280. {
  281. name: "error on unindexed arguments",
  282. args: args{
  283. createObj: func() interface{} { return &int256Struct{} },
  284. resultObj: func() interface{} { return &int256Struct{} },
  285. resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
  286. fields: Arguments{Argument{
  287. Name: "int256Value",
  288. Type: int256Type,
  289. Indexed: false,
  290. }},
  291. topics: []common.Hash{
  292. {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  293. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
  294. },
  295. },
  296. wantErr: true,
  297. },
  298. {
  299. name: "error on tuple in topic reconstruction",
  300. args: args{
  301. createObj: func() interface{} { return &tupleType },
  302. resultObj: func() interface{} { return &tupleType },
  303. resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
  304. fields: Arguments{Argument{
  305. Name: "tupletype",
  306. Type: tupleType,
  307. Indexed: true,
  308. }},
  309. topics: []common.Hash{{0}},
  310. },
  311. wantErr: true,
  312. },
  313. {
  314. name: "error on improper encoded function",
  315. args: args{
  316. createObj: func() interface{} { return &funcStruct{} },
  317. resultObj: func() interface{} { return &funcStruct{} },
  318. resultMap: func() map[string]interface{} {
  319. return make(map[string]interface{})
  320. },
  321. fields: Arguments{Argument{
  322. Name: "funcValue",
  323. Type: funcType,
  324. Indexed: true,
  325. }},
  326. topics: []common.Hash{
  327. {0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255,
  328. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
  329. },
  330. },
  331. wantErr: true,
  332. },
  333. }
  334. return tests
  335. }
  336. func TestParseTopics(t *testing.T) {
  337. tests := setupTopicsTests()
  338. for _, tt := range tests {
  339. t.Run(tt.name, func(t *testing.T) {
  340. createObj := tt.args.createObj()
  341. if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
  342. t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
  343. }
  344. resultObj := tt.args.resultObj()
  345. if !reflect.DeepEqual(createObj, resultObj) {
  346. t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
  347. }
  348. })
  349. }
  350. }
  351. func TestParseTopicsIntoMap(t *testing.T) {
  352. tests := setupTopicsTests()
  353. for _, tt := range tests {
  354. t.Run(tt.name, func(t *testing.T) {
  355. outMap := make(map[string]interface{})
  356. if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
  357. t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
  358. }
  359. resultMap := tt.args.resultMap()
  360. if !reflect.DeepEqual(outMap, resultMap) {
  361. t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
  362. }
  363. })
  364. }
  365. }