validator.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. const { isAddress, toChecksumAddress } = require('web3-utils')
  2. const { getInstance } = require('./utils')
  3. const { rewardAccount } = require('./config')
  4. const Ajv = require('ajv')
  5. const ajv = new Ajv({ format: 'fast' })
  6. ajv.addKeyword('isAddress', {
  7. validate: (schema, data) => {
  8. try {
  9. return isAddress(data)
  10. } catch (e) {
  11. return false
  12. }
  13. },
  14. errors: true,
  15. })
  16. ajv.addKeyword('isKnownContract', {
  17. validate: (schema, data) => {
  18. try {
  19. return getInstance(data) !== null
  20. } catch (e) {
  21. return false
  22. }
  23. },
  24. errors: true,
  25. })
  26. ajv.addKeyword('isFeeRecipient', {
  27. validate: (schema, data) => {
  28. try {
  29. return toChecksumAddress(rewardAccount) === toChecksumAddress(data)
  30. } catch (e) {
  31. return false
  32. }
  33. },
  34. errors: true,
  35. })
  36. const addressType = { type: 'string', pattern: '^0x[a-fA-F0-9]{40}$', isAddress: true }
  37. const proofType = { type: 'string', pattern: '^0x[a-fA-F0-9]{512}$' }
  38. const encryptedAccountType = { type: 'string', pattern: '^0x[a-fA-F0-9]{392}$' }
  39. const bytes32Type = { type: 'string', pattern: '^0x[a-fA-F0-9]{64}$' }
  40. const instanceType = { ...addressType, isKnownContract: true }
  41. const relayerType = { ...addressType, isFeeRecipient: true }
  42. const tornadoWithdrawSchema = {
  43. type: 'object',
  44. properties: {
  45. proof: proofType,
  46. contract: instanceType,
  47. args: {
  48. type: 'array',
  49. maxItems: 6,
  50. minItems: 6,
  51. items: [bytes32Type, bytes32Type, addressType, relayerType, bytes32Type, bytes32Type],
  52. },
  53. },
  54. additionalProperties: false,
  55. required: ['proof', 'contract', 'args'],
  56. }
  57. const miningRewardSchema = {
  58. type: 'object',
  59. properties: {
  60. proof: proofType,
  61. args: {
  62. type: 'object',
  63. properties: {
  64. rate: bytes32Type,
  65. fee: bytes32Type,
  66. instance: instanceType,
  67. rewardNullifier: bytes32Type,
  68. extDataHash: bytes32Type,
  69. depositRoot: bytes32Type,
  70. withdrawalRoot: bytes32Type,
  71. extData: {
  72. type: 'object',
  73. properties: {
  74. relayer: relayerType,
  75. encryptedAccount: encryptedAccountType,
  76. },
  77. additionalProperties: false,
  78. required: ['relayer', 'encryptedAccount'],
  79. },
  80. account: {
  81. type: 'object',
  82. properties: {
  83. inputRoot: bytes32Type,
  84. inputNullifierHash: bytes32Type,
  85. outputRoot: bytes32Type,
  86. outputPathIndices: bytes32Type,
  87. outputCommitment: bytes32Type,
  88. },
  89. additionalProperties: false,
  90. required: [
  91. 'inputRoot',
  92. 'inputNullifierHash',
  93. 'outputRoot',
  94. 'outputPathIndices',
  95. 'outputCommitment',
  96. ],
  97. },
  98. },
  99. additionalProperties: false,
  100. required: [
  101. 'rate',
  102. 'fee',
  103. 'instance',
  104. 'rewardNullifier',
  105. 'extDataHash',
  106. 'depositRoot',
  107. 'withdrawalRoot',
  108. 'extData',
  109. 'account',
  110. ],
  111. },
  112. },
  113. additionalProperties: false,
  114. required: ['proof', 'args'],
  115. }
  116. const miningWithdrawSchema = {
  117. type: 'object',
  118. properties: {
  119. proof: proofType,
  120. args: {
  121. type: 'object',
  122. properties: {
  123. amount: bytes32Type,
  124. extDataHash: bytes32Type,
  125. extData: {
  126. type: 'object',
  127. properties: {
  128. fee: bytes32Type,
  129. recipient: addressType,
  130. relayer: relayerType,
  131. encryptedAccount: encryptedAccountType,
  132. },
  133. additionalProperties: false,
  134. required: ['fee', 'relayer', 'encryptedAccount', 'recipient'],
  135. },
  136. account: {
  137. type: 'object',
  138. properties: {
  139. inputRoot: bytes32Type,
  140. inputNullifierHash: bytes32Type,
  141. outputRoot: bytes32Type,
  142. outputPathIndices: bytes32Type,
  143. outputCommitment: bytes32Type,
  144. },
  145. additionalProperties: false,
  146. required: [
  147. 'inputRoot',
  148. 'inputNullifierHash',
  149. 'outputRoot',
  150. 'outputPathIndices',
  151. 'outputCommitment',
  152. ],
  153. },
  154. },
  155. additionalProperties: false,
  156. required: ['amount', 'extDataHash', 'extData', 'account'],
  157. },
  158. },
  159. additionalProperties: false,
  160. required: ['proof', 'args'],
  161. }
  162. const validateTornadoWithdraw = ajv.compile(tornadoWithdrawSchema)
  163. const validateMiningReward = ajv.compile(miningRewardSchema)
  164. const validateMiningWithdraw = ajv.compile(miningWithdrawSchema)
  165. function getInputError(validator, data) {
  166. validator(data)
  167. if (validator.errors) {
  168. const error = validator.errors[0]
  169. return `${error.dataPath} ${error.message}`
  170. }
  171. return null
  172. }
  173. function getTornadoWithdrawInputError(data) {
  174. return getInputError(validateTornadoWithdraw, data)
  175. }
  176. function getMiningRewardInputError(data) {
  177. return getInputError(validateMiningReward, data)
  178. }
  179. function getMiningWithdrawInputError(data) {
  180. return getInputError(validateMiningWithdraw, data)
  181. }
  182. module.exports = {
  183. getTornadoWithdrawInputError,
  184. getMiningRewardInputError,
  185. getMiningWithdrawInputError,
  186. }