diff --git a/.gitignore b/.gitignore index 4d89ac8..12b96aa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __debug_* .DS_Store *secret* *.pem +*Key.txt diff --git a/adapters/node/.gitignore b/adapters/node/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/adapters/node/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/adapters/node/generate-keys.js b/adapters/node/generate-keys.js index cc19118..e9993b7 100644 --- a/adapters/node/generate-keys.js +++ b/adapters/node/generate-keys.js @@ -1,28 +1,28 @@ const crypto = require('crypto'); -const path = require('path'); const fs = require('fs'); +const path = require('path'); + +// Generate a random private key +let privKey; +do { + privKey = crypto.randomBytes(32); +} while (crypto.createECDH('secp256k1').setPrivateKey(privKey, 'hex').getPublicKey() === null); + +// Get the ECDH object and set the private key +const ecdh = crypto.createECDH('secp256k1'); +ecdh.setPrivateKey(privKey); -// Generate a secp256k1 key pair -const keyPair = crypto.generateKeyPairSync('ec', { - namedCurve: 'secp256k1' // Name of the curve -}); +// Get the public key in uncompressed format +const pubKey = ecdh.getPublicKey('hex', 'uncompressed'); -// Export the private key as a PEM-formatted string -const privateKey = keyPair.privateKey.export({ - type: 'sec1', - format: 'pem', -}); +const privateKeyPath = path.join(__dirname, 'privateKey.txt'); +const publicKeyPath = path.join(__dirname, 'publicKey.txt'); -// Export the public key as a PEM-formatted string -const publicKey = keyPair.publicKey.export({ - type: 'spki', - format: 'pem', -}); +// Write private key to disk +fs.writeFileSync(privateKeyPath, privKey.toString('hex')); -// Specify the paths where the keys will be saved -const privateKeyPath = path.resolve(__dirname, 'privateKey.pem') -const publicKeyPath = path.resolve(__dirname, 'publicKey.pem') +// Write public key to disk +fs.writeFileSync(publicKeyPath, pubKey); -// Write the keys to disk -fs.writeFileSync(privateKeyPath, privateKey); -fs.writeFileSync(publicKeyPath, publicKey); \ No newline at end of file +console.log(`Private key saved to ${privateKeyPath}`); +console.log(`Public key saved to ${publicKeyPath}`); \ No newline at end of file diff --git a/adapters/node/package-lock.json b/adapters/node/package-lock.json new file mode 100644 index 0000000..0d1a6d5 --- /dev/null +++ b/adapters/node/package-lock.json @@ -0,0 +1,338 @@ +{ + "name": "signing", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "signing", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@metamask/eth-sig-util": "^7.0.0" + } + }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@metamask/abi-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@metamask/abi-utils/-/abi-utils-2.0.2.tgz", + "integrity": "sha512-B/A1dY/w4F/t6cDHUscklO6ovb/ztFsrsTXFd8QlqSByk/vyy+QbPE3VVpmmyI/7RX+PA1AJcvBdzCIz+r9dVQ==", + "dependencies": { + "@metamask/utils": "^8.0.0", + "superstruct": "^1.0.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.0.tgz", + "integrity": "sha512-8KeXZB4SKx3EfNS5ahbjUMegyGvDQYk6Nk3hmM658sXpfAQR5ZlIXBgj+9RF+ZROqsU6EuNVgKt7Fr10re60PQ==", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "@metamask/abi-utils": "^2.0.2", + "@metamask/utils": "^8.1.0", + "ethereum-cryptography": "^2.1.2", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": "^16.20 || ^18.16 || >=20" + } + }, + "node_modules/@metamask/utils": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.2.1.tgz", + "integrity": "sha512-dlnpow8r0YHDDL1xKCEwUoTGOAo9icdv+gaJG0EbgDnkD/BDqW2eH1XMtm9i7rPaiHWo/aLtcrh9WBhkCq/viw==", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "superstruct": "^1.0.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", + "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/pony-cause": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.10.tgz", + "integrity": "sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/superstruct": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz", + "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/adapters/node/package.json b/adapters/node/package.json new file mode 100644 index 0000000..1c65ca8 --- /dev/null +++ b/adapters/node/package.json @@ -0,0 +1,15 @@ +{ + "name": "signing", + "version": "1.0.0", + "description": "", + "main": "generate-keys.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@metamask/eth-sig-util": "^7.0.0" + } +} diff --git a/adapters/node/sign.js b/adapters/node/sign.js index d16dbf4..3b91d83 100644 --- a/adapters/node/sign.js +++ b/adapters/node/sign.js @@ -1,32 +1,62 @@ -const path = require('path'); -const crypto = require('crypto'); const fs = require('fs'); +const path = require('path'); +const { + signTypedData +} = require("@metamask/eth-sig-util"); -// Load the private key from disk -const privateKeyPath = path.resolve(__dirname, 'privateKey.pem') -const privateKey = fs.readFileSync(privateKeyPath, 'utf8'); - -const requestPayload = { - symbol: "BTCUSD", - orderType: "limit", - side: "buy", - quantity: "1.0", - price: "50000.00", - timestamp: "1697813554" -}; +// Define the path to the private key file +const privateKeyPath = path.join(__dirname, 'privateKey.txt'); -const jsonString = JSON.stringify(requestPayload); +// Read the private key from the file +const privateKeyHex = fs.readFileSync(privateKeyPath, 'utf8'); -// Create a hash of the JSON string -const hash = crypto.createHash('sha256').update(jsonString).digest(); -// Create a signing object -const sign = crypto.createSign('SHA256'); +const order = { + price: "20.99", + size: "1000", + symbol: "BTC-ETH", + side: "sell", + clientOrderId: "a677273e-12de-4acc-a4f8-de7fb5b86e37" +}; -// Update the signing object with the hash -sign.update(hash); +const domain = { + name: 'orderbook', + version: '1.0', + chainId: 1, // Mainnet ID, change accordingly if you're using a different network + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' // Replace with your contract address +}; + +const types = { + Order: [ + { name: 'price', type: 'string' }, + { name: 'size', type: 'string' }, + { name: 'symbol', type: 'string' }, + { name: 'side', type: 'string' }, + { name: 'clientOrderId', type: 'string' } + ] +}; + +const data = { + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' } + ], + ...types + }, + primaryType: 'Order', + domain: domain, + message: order +}; -// Generate the signature using the private key -const signature = sign.sign(privateKey, 'hex'); +// Sign the EIP-712 structured data +const signature = signTypedData({ + // Remove the 0x prefix if present + privateKey: Buffer.from(privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex, "hex"), + data: data, + version: "V4", +}); -console.log('signature :', signature); \ No newline at end of file +console.log('EIP-712 Signature:', signature); \ No newline at end of file diff --git a/adapters/node/verify-signature.js b/adapters/node/verify-signature.js deleted file mode 100644 index e297ed6..0000000 --- a/adapters/node/verify-signature.js +++ /dev/null @@ -1,35 +0,0 @@ -const path = require('path'); -const crypto = require('crypto'); -const fs = require('fs'); - -const requestPayload = { - symbol: "BTCUSD", - orderType: "limit", - side: "buy", - quantity: "1.0", - price: "50000.00", - timestamp: "1697813554" -}; - -const signature = "30440220306c22dc5ab8c650d7bb59934f140d33d089e2ff05bb5de889ccf531a5591dff0220673cd5d2bc2ce5c815917cc680cd2076a8b560d4ab4305531a84791ea937ade7" - -// Load the public key from disk -const publicKeyPath = path.resolve(__dirname, 'publicKey.pem'); -const publicKey = fs.readFileSync(publicKeyPath, 'utf8'); -console.log('publicKey :', publicKey); - -const jsonString = JSON.stringify(requestPayload); - -// Create a hash of the JSON string -const hash = crypto.createHash('sha256').update(jsonString).digest(); - -// Create a verify object -const verify = crypto.createVerify('SHA256'); - -// Update the verify object with the hash -verify.update(hash); - -// Verify the signature -const isSignatureValid = verify.verify(publicKey, signature, 'hex'); - -console.log('Is signature valid?', isSignatureValid); \ No newline at end of file diff --git a/cmd/order-book/main.go b/cmd/order-book/main.go index 6717381..95563df 100644 --- a/cmd/order-book/main.go +++ b/cmd/order-book/main.go @@ -42,7 +42,10 @@ func setup() { log.Fatalf("error creating repository: %v", err) } - service, err := service.New(repository) + // TODO: add CLI flag to easily switch between blockchains + ethClient := &service.EthereumClient{} + + service, err := service.New(repository, ethClient) if err != nil { log.Fatalf("error creating service: %v", err) } diff --git a/go.mod b/go.mod index 96b96ba..4badd5d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/orbs-network/order-book go 1.21.1 require ( + github.com/ethereum/go-ethereum v1.13.5 github.com/go-redis/redismock/v9 v9.2.0 github.com/google/uuid v1.3.1 github.com/gorilla/mux v1.8.0 @@ -14,14 +15,29 @@ require ( ) require ( + github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/holiman/uint256 v1.2.3 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/onsi/gomega v1.28.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/supranational/blst v0.3.11 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 0a48d1e..2888777 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,139 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw= github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -46,15 +144,28 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/mocks/blockchain_client.go b/mocks/blockchain_client.go new file mode 100644 index 0000000..21723ab --- /dev/null +++ b/mocks/blockchain_client.go @@ -0,0 +1,16 @@ +package mocks + +import ( + "context" + + "github.com/orbs-network/order-book/service" +) + +type MockBcClient struct { + IsVerified bool + Error error +} + +func (m *MockBcClient) VerifySignature(ctx context.Context, input service.VerifySignatureInput) (bool, error) { + return m.IsVerified, m.Error +} diff --git a/scripts/coin-gecko/main.go b/scripts/coin-gecko/main.go new file mode 100644 index 0000000..7f4d479 --- /dev/null +++ b/scripts/coin-gecko/main.go @@ -0,0 +1,100 @@ +// Takes a list of supported tokens on Polygon and formats them into a JSON file. +// Usage: go run scripts/coin-gecko/main.go + +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" +) + +const endpoint = "https://tokens.coingecko.com/polygon-pos/all.json" + +type Token struct { + ChainID int `json:"chainId"` + Address string `json:"address"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals int `json:"decimals"` + LogoURI string `json:"logoURI"` +} + +func main() { + resp, err := http.Get(endpoint) + if err != nil { + fmt.Println("Error fetching data:", err) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + fmt.Println("Failed to fetch data. Status code:", resp.StatusCode) + return + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response:", err) + return + } + + var data map[string]interface{} + err = json.Unmarshal(body, &data) + if err != nil { + fmt.Println("Error decoding JSON:", err) + return + } + + tokens, ok := data["tokens"].([]interface{}) + if !ok { + fmt.Println("No tokens found in the response.") + return + } + + formattedTokens := make(map[string]map[string]interface{}) + + for _, token := range tokens { + tok, ok := token.(map[string]interface{}) + if !ok { + continue + } + + symbol, ok := tok["symbol"].(string) + if !ok { + continue + } + + address, ok := tok["address"].(string) + if !ok { + continue + } + + decimals, ok := tok["decimals"].(float64) + if !ok { + continue + } + + formattedTokens[symbol] = map[string]interface{}{ + "address": address, + "decimals": decimals, + } + } + + outputFile := "supportedTokens.json" + outputJSON, err := json.MarshalIndent(formattedTokens, "", " ") + if err != nil { + fmt.Println("Error encoding JSON:", err) + return + } + + err = os.WriteFile(outputFile, outputJSON, 0644) + if err != nil { + fmt.Println("Error writing to file:", err) + return + } + + fmt.Printf("Formatted tokens saved to %s\n", outputFile) +} diff --git a/scripts/signing/main.go b/scripts/signing/main.go new file mode 100644 index 0000000..cd88312 --- /dev/null +++ b/scripts/signing/main.go @@ -0,0 +1,37 @@ +// A quick script to test EIP712 signature verification +// Usage: go run scripts/signing/main.go + +package main + +import ( + "context" + "fmt" + "log" + + "github.com/orbs-network/order-book/service" +) + +var sig = "0x977939edfd70b5e0a802b0874fa681749ac3669516f8250fc11426bd3d02e3f353a874aec1d1394965507c7404a24a53cc4427917c2b214a695ea432aa79ccba1b" + +var pubKey = "0x6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb336b6fbcb60b5b3d4f1551ac45e5ffc4936466e7d98f6c7c0ec736539f74691a6" + +var message = `{"permitted": {"token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "amount": 10000000}, "spender": "0x21Da9737764527e75C17F1AB26Cb668b66dEE0a0", "nonce": 557404199, "deadline": 1709481491, "witness": {"info": {"reactor": "0x21Da9737764527e75C17F1AB26Cb668b66dEE0a0", "swapper": "0xE3682CCecefBb3C3fe524BbFF1598B2BBaC0d6E3", "nonce": 557404199, "deadline": 1709481491, "additionalValidationContract": "0x1a08D64Fb4a7D0b6DA5606A1e4619c147C3fB95e", "additionalValidationData": "0x"}, "exclusiveFiller": "0x1a08D64Fb4a7D0b6DA5606A1e4619c147C3fB95e", "exclusivityOverrideBps": 0, "input": {"token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "amount": 10000000}, "outputs": [{"token": "0x11cd37bb86f65419713f30673a480ea33c826872", "amount": 1000000000000000, "recipient": "0x8fd379246834eac74B8419FfdA202CF8051F7A03"}]}}` + +func main() { + + client := service.EthereumClient{} + + ctx := context.Background() + + verified, err := client.VerifySignature(ctx, service.VerifySignatureInput{ + MessageData: message, + Signature: sig, + PublicKey: pubKey, + }) + + if err != nil { + log.Fatalf("Error verifying signature: %v", err) + } + + fmt.Printf("Signature verified: %v\n", verified) +} diff --git a/service/cancel_order_test.go b/service/cancel_order_test.go index e17a98a..a6e55ba 100644 --- a/service/cancel_order_test.go +++ b/service/cancel_order_test.go @@ -20,6 +20,8 @@ func TestService_CancelOrder(t *testing.T) { clientOId := uuid.MustParse("f577273e-12de-4acc-a4f8-de7fb5b86e37") order := &models.Order{Id: orderId, UserId: userId, Status: models.STATUS_OPEN, ClientOId: clientOId} + mockBcClient := &mocks.MockBcClient{IsVerified: true} + testCases := []struct { name string order *models.Order @@ -41,7 +43,7 @@ func TestService_CancelOrder(t *testing.T) { for _, c := range testCases { t.Run(c.name, func(t *testing.T) { fmt.Print(c.name) - svc, _ := service.New(&mocks.MockOrderBookStore{Order: c.order, Error: c.err}) + svc, _ := service.New(&mocks.MockOrderBookStore{Order: c.order, Error: c.err}, mockBcClient) input := service.CancelOrderInput{Id: orderId, IsClientOId: false, UserId: userId} diff --git a/service/cancel_orders_for_user_test.go b/service/cancel_orders_for_user_test.go index 1f5b002..739c6cc 100644 --- a/service/cancel_orders_for_user_test.go +++ b/service/cancel_orders_for_user_test.go @@ -11,11 +11,12 @@ import ( func TestService_CancelOrdersForUser(t *testing.T) { ctx := context.Background() + mockBcClient := &mocks.MockBcClient{IsVerified: true} t.Run("should successfully cancel all orders for a user", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: &mocks.User} - s, _ := service.New(store) + s, _ := service.New(store, mockBcClient) err := s.CancelOrdersForUser(ctx, mocks.UserId) assert.Equal(t, err, nil) @@ -24,7 +25,7 @@ func TestService_CancelOrdersForUser(t *testing.T) { t.Run("should return error on unexpected error", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: nil, Error: assert.AnError} - s, _ := service.New(store) + s, _ := service.New(store, mockBcClient) err := s.CancelOrdersForUser(ctx, mocks.UserId) assert.ErrorContains(t, err, "could not cancel orders for user") diff --git a/service/ethereum.go b/service/ethereum.go new file mode 100644 index 0000000..64161d0 --- /dev/null +++ b/service/ethereum.go @@ -0,0 +1,182 @@ +package service + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/orbs-network/order-book/utils/logger" + "github.com/orbs-network/order-book/utils/logger/logctx" +) + +type EthereumClient struct{} + +type VerifySignatureInput struct { + // A JSON string representing the message data that was signed + MessageData string + // The public key of the user that purportedly signed the message + PublicKey string + // The signature of the message + Signature string +} + +// Returns true if the signature is valid (the `PublicKey` matches the recovered one from the `Signature`), false otherwise +// +// https://blog.hook.xyz/validate-eip-712/ +func (e *EthereumClient) VerifySignature(ctx context.Context, input VerifySignatureInput) (bool, error) { + // Prepend "04" to the public key to ensure it's in the uncompressed format + fullPubKey := "04" + strings.TrimPrefix(input.PublicKey, "0x") + + // Decode the hex-encoded public key + pubKeyBytes, err := hex.DecodeString(fullPubKey) + if err != nil { + logctx.Error(ctx, "error decoding hex public key", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("error decoding hex public key: %v", err) + } + + // Validate the format and length of the public key + if len(pubKeyBytes) != 65 || pubKeyBytes[0] != 4 { + logctx.Error(ctx, "invalid public key format", logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("invalid public key format") + } + + // Convert the byte representation of the public key to an ECDSA public key + pubKey, err := crypto.UnmarshalPubkey(pubKeyBytes) + if err != nil { + logctx.Error(ctx, "failed to unmarshal public key", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to unmarshal public key: %v", err) + } + + // Decode the hex-encoded signature + signatureBytes, err := hex.DecodeString(strings.TrimPrefix(input.Signature, "0x")) + if err != nil { + logctx.Error(ctx, "error decoding hex signature", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to decode signature: %v", err) + } + + // Normalize the `v` value in the signature (adjust for Ethereum's signature format) + v := signatureBytes[64] + if v == 27 || v == 28 { + logctx.Info(ctx, "signature v value is normalized", logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + v -= 27 + signatureBytes[64] = v + } + + // EIP712 domain + domain := apitypes.TypedDataDomain{ + Name: "Permit2", + ChainId: math.NewHexOrDecimal256(137), + VerifyingContract: "0x000000000022d473030f116ddee9f6b43ac78ba3", + } + + // TODO: confirm with Zlotin rePermit payload + // EIP712 message types + types := apitypes.Types{ + "EIP712Domain": { + {Name: "name", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + "PermitWitnessTransferFrom": { + {Name: "permitted", Type: "TokenPermissions"}, + {Name: "spender", Type: "address"}, + {Name: "nonce", Type: "uint256"}, + {Name: "deadline", Type: "uint256"}, + {Name: "witness", Type: "PartialOrder"}, + }, + "TokenPermissions": { + {Name: "token", Type: "address"}, + {Name: "amount", Type: "uint256"}, + }, + "PartialOrder": { + {Name: "info", Type: "OrderInfo"}, + {Name: "exclusiveFiller", Type: "address"}, + {Name: "exclusivityOverrideBps", Type: "uint256"}, + {Name: "input", Type: "PartialInput"}, + {Name: "outputs", Type: "PartialOutput[]"}, + }, + "OrderInfo": { + {Name: "reactor", Type: "address"}, + {Name: "swapper", Type: "address"}, + {Name: "nonce", Type: "uint256"}, + {Name: "deadline", Type: "uint256"}, + {Name: "additionalValidationContract", Type: "address"}, + {Name: "additionalValidationData", Type: "bytes"}, + }, + "PartialInput": { + {Name: "token", Type: "address"}, + {Name: "amount", Type: "uint256"}, + }, + "PartialOutput": { + {Name: "token", Type: "address"}, + {Name: "amount", Type: "uint256"}, + {Name: "recipient", Type: "address"}, + }, + } + + // Unmarshal the message + var message map[string]interface{} + if err := json.Unmarshal([]byte(input.MessageData), &message); err != nil { + logctx.Error(ctx, "failed to unmarshal message", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to unmarshal message: %v", err) + } + + // Create the TypedData object + typedData := apitypes.TypedData{ + PrimaryType: "PermitWitnessTransferFrom", + Types: types, + Domain: domain, + Message: message, + } + + // Hash the message data + dataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + logctx.Error(ctx, "failed to hash structured data", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to hash structured data: %v", err) + } + + // Hash the domain separator + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + logctx.Error(ctx, "failed to hash domain separator", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to hash domain separator: %v", err) + } + + // Reconstruct the exact message that was signed - concatenate the domain separator and the hash of the data + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(dataHash))) + + // Compute the Keccak256 hash of the final message + hashBytes := crypto.Keccak256(rawData) + hash := common.BytesToHash(hashBytes) + + // Recover the public key from the signature + recoveredPub, err := crypto.SigToPub(hash.Bytes(), signatureBytes) + if err != nil { + logctx.Error(ctx, "failed to recover public key from signature", logger.Error(err), logger.String("publicKey", fullPubKey), logger.String("signature", input.Signature), logger.String("messageData", input.MessageData)) + return false, fmt.Errorf("failed to recover public key from signature: %v", err) + } + + // Convert the recovered public key to bytes + recoveredPubBytes := crypto.FromECDSAPub(recoveredPub) + + // Convert the original public key to bytes + originalPubBytes := crypto.FromECDSAPub(pubKey) + + // Compare the recovered public key with the original public key + if !bytes.Equal(recoveredPubBytes, originalPubBytes) { + logctx.Warn(ctx, "signature does not match", logger.String("recoveredPub", hex.EncodeToString(recoveredPubBytes)), logger.String("originalPub", hex.EncodeToString(originalPubBytes))) + return false, fmt.Errorf("signature does not match") + } + + logctx.Info(ctx, "signature is valid", logger.String("recoveredPub", hex.EncodeToString(recoveredPubBytes)), logger.String("originalPub", hex.EncodeToString(originalPubBytes))) + // Signature is valid + return true, nil +} diff --git a/service/ethereum_test.go b/service/ethereum_test.go new file mode 100644 index 0000000..9fa6a7a --- /dev/null +++ b/service/ethereum_test.go @@ -0,0 +1,58 @@ +package service + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +var sig = "0xdbf6d13ed9b1af881499ce25b4a9f40604c74b65ea1a871edec9e762950a4460502d126fe40b23f530caf4af7dc2f8629014b64a12b94fd0cb17c5569b2a05661c" + +var pubKey = "0x6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb336b6fbcb60b5b3d4f1551ac45e5ffc4936466e7d98f6c7c0ec736539f74691a6" + +var message = `{"permitted": {"token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "amount": "20000000000"}, "spender": "0x21Da9737764527e75C17F1AB26Cb668b66dEE0a0", "nonce": 845753781, "deadline": 1709657651, "witness": {"info": {"reactor": "0x21Da9737764527e75C17F1AB26Cb668b66dEE0a0", "swapper": "0xE3682CCecefBb3C3fe524BbFF1598B2BBaC0d6E3", "nonce": 845753781, "deadline": 1709657651, "additionalValidationContract": "0x1a08D64Fb4a7D0b6DA5606A1e4619c147C3fB95e", "additionalValidationData": "0x"}, "exclusiveFiller": "0x1a08D64Fb4a7D0b6DA5606A1e4619c147C3fB95e", "exclusivityOverrideBps": 0, "input": {"token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "amount": "20000000000"}, "outputs": [{"token": "0x11cd37bb86f65419713f30673a480ea33c826872", "amount": "10000000000000000000", "recipient": "0x8fd379246834eac74B8419FfdA202CF8051F7A03"}]}}` + +func TestEthereumClient_VerifySignature(t *testing.T) { + ctx := context.TODO() + ethereumClient := &EthereumClient{} + + t.Run("successfully verify signature - should return true", func(t *testing.T) { + input := VerifySignatureInput{ + PublicKey: pubKey, + Signature: sig, + MessageData: message, + } + + result, err := ethereumClient.VerifySignature(ctx, input) + + assert.NoError(t, err) + assert.True(t, result) + }) + + t.Run("invalid public key - should return false", func(t *testing.T) { + input := VerifySignatureInput{ + PublicKey: "0x123", + Signature: sig, + MessageData: message, + } + + result, err := ethereumClient.VerifySignature(ctx, input) + + assert.ErrorContains(t, err, "error decoding hex public key") + assert.False(t, result) + }) + + t.Run("failed to unmarshal message - should return false", func(t *testing.T) { + input := VerifySignatureInput{ + PublicKey: pubKey, + Signature: sig, + MessageData: "invalid message", + } + + result, err := ethereumClient.VerifySignature(ctx, input) + + assert.ErrorContains(t, err, "failed to unmarshal message") + assert.False(t, result) + }) +} diff --git a/service/get_order_by_client_oid_test.go b/service/get_order_by_client_oid_test.go index 5927e8e..3697bfd 100644 --- a/service/get_order_by_client_oid_test.go +++ b/service/get_order_by_client_oid_test.go @@ -17,9 +17,11 @@ func TestService_GetOrderByClientOId(t *testing.T) { clientOId := uuid.MustParse("e577273e-12de-4acc-a4f8-de7fb5b86e37") + mockBcClient := &mocks.MockBcClient{IsVerified: true} + t.Run("successfully retrieve order by client order ID - should return order", func(t *testing.T) { o := &models.Order{ClientOId: clientOId} - svc, _ := service.New(&mocks.MockOrderBookStore{Order: o}) + svc, _ := service.New(&mocks.MockOrderBookStore{Order: o}, mockBcClient) order, err := svc.GetOrderByClientOId(ctx, clientOId) @@ -28,7 +30,7 @@ func TestService_GetOrderByClientOId(t *testing.T) { }) t.Run("order not found - should return nil", func(t *testing.T) { - svc, _ := service.New(&mocks.MockOrderBookStore{Error: models.ErrOrderNotFound}) + svc, _ := service.New(&mocks.MockOrderBookStore{Error: models.ErrOrderNotFound}, mockBcClient) order, err := svc.GetOrderByClientOId(ctx, clientOId) @@ -37,7 +39,7 @@ func TestService_GetOrderByClientOId(t *testing.T) { }) t.Run("unexpected error - should return error", func(t *testing.T) { - svc, _ := service.New(&mocks.MockOrderBookStore{Error: assert.AnError}) + svc, _ := service.New(&mocks.MockOrderBookStore{Error: assert.AnError}, mockBcClient) order, err := svc.GetOrderByClientOId(ctx, clientOId) diff --git a/service/get_symbols_test.go b/service/get_symbols_test.go index 40b5b29..f5dd5e8 100644 --- a/service/get_symbols_test.go +++ b/service/get_symbols_test.go @@ -11,7 +11,7 @@ import ( func TestService_GetSymbols(t *testing.T) { ctx := context.Background() - svc, _ := service.New(&mocks.MockOrderBookStore{}) + svc, _ := service.New(&mocks.MockOrderBookStore{}, &mocks.MockBcClient{}) symbols, err := svc.GetSymbols(ctx) diff --git a/service/get_user_by_public_key_test.go b/service/get_user_by_public_key_test.go index ef47a6c..a5fd8ee 100644 --- a/service/get_user_by_public_key_test.go +++ b/service/get_user_by_public_key_test.go @@ -13,11 +13,12 @@ import ( func TestService_GetUserByPublicKey(t *testing.T) { ctx := context.Background() + mockBcClient := &mocks.MockBcClient{IsVerified: true} t.Run("should get a user by their public key", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: &mocks.User} - svc, _ := service.New(store) + svc, _ := service.New(store, mockBcClient) user, _ := svc.GetUserByPublicKey(ctx, mocks.PubKey) @@ -27,7 +28,7 @@ func TestService_GetUserByPublicKey(t *testing.T) { t.Run("should return error if user not found", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: nil, ErrUser: models.ErrUserNotFound} - svc, _ := service.New(store) + svc, _ := service.New(store, mockBcClient) user, err := svc.GetUserByPublicKey(ctx, mocks.PubKey) @@ -38,7 +39,7 @@ func TestService_GetUserByPublicKey(t *testing.T) { t.Run("should return error on unexpected error getting user by public key", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: nil, ErrUser: assert.AnError} - svc, _ := service.New(store) + svc, _ := service.New(store, mockBcClient) user, err := svc.GetUserByPublicKey(ctx, mocks.PubKey) @@ -49,7 +50,7 @@ func TestService_GetUserByPublicKey(t *testing.T) { t.Run("should return error if user is nil but no error", func(t *testing.T) { store := &mocks.MockOrderBookStore{User: nil, ErrUser: nil} - svc, _ := service.New(store) + svc, _ := service.New(store, mockBcClient) user, err := svc.GetUserByPublicKey(ctx, mocks.PubKey) diff --git a/service/process_order_test.go b/service/process_order_test.go index 65818e6..cf94d48 100644 --- a/service/process_order_test.go +++ b/service/process_order_test.go @@ -16,6 +16,7 @@ import ( func TestService_ProcessOrder(t *testing.T) { ctx := context.Background() + mockBcClient := &mocks.MockBcClient{IsVerified: true} symbol, _ := models.StrToSymbol("USDC-ETH") userId := uuid.MustParse("a577273e-12de-4acc-a4f8-de7fb5b86e37") @@ -40,7 +41,7 @@ func TestService_ProcessOrder(t *testing.T) { ClientOrderID: orderId, } - svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Error: assert.AnError}) + svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Error: assert.AnError}, mockBcClient) order, err := svc.ProcessOrder(ctx, input) @@ -58,7 +59,7 @@ func TestService_ProcessOrder(t *testing.T) { ClientOrderID: orderId, } - svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: nil}) + svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: nil}, mockBcClient) newOrder, err := svc.ProcessOrder(ctx, input) @@ -85,7 +86,7 @@ func TestService_ProcessOrder(t *testing.T) { ClientOrderID: orderId, } - svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: &models.Order{UserId: uuid.MustParse("b577273e-12de-4acc-a4f8-de7fb5b86e37")}}) + svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: &models.Order{UserId: uuid.MustParse("b577273e-12de-4acc-a4f8-de7fb5b86e37")}}, mockBcClient) order, err := svc.ProcessOrder(ctx, input) @@ -103,7 +104,7 @@ func TestService_ProcessOrder(t *testing.T) { ClientOrderID: orderId, } - svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: &models.Order{ClientOId: orderId, UserId: userId}}) + svc, _ := service.New(&mocks.MockOrderBookStore{User: &user, Order: &models.Order{ClientOId: orderId, UserId: userId}}, mockBcClient) order, err := svc.ProcessOrder(ctx, input) diff --git a/service/service.go b/service/service.go index d99bd9d..62841be 100644 --- a/service/service.go +++ b/service/service.go @@ -30,16 +30,25 @@ type OrderBookService interface { GetAmountOut(ctx context.Context, auctionID uuid.UUID, symbol models.Symbol, side models.Side, amountIn decimal.Decimal) (models.AmountOut, error) } +type BlockChainService interface { + VerifySignature(ctx context.Context, input VerifySignatureInput) (bool, error) +} + // Service contains methods that implement the business logic for the application. type Service struct { - orderBookStore store.OrderBookStore + orderBookStore store.OrderBookStore + blockchainClient BlockChainService } // New creates a new Service with injected dependencies. -func New(store store.OrderBookStore) (*Service, error) { +func New(store store.OrderBookStore, bcClient BlockChainService) (*Service, error) { if store == nil { return nil, errors.New("store cannot be nil") } - return &Service{orderBookStore: store}, nil + if bcClient == nil { + return nil, errors.New("bcClient cannot be nil") + } + + return &Service{orderBookStore: store, blockchainClient: bcClient}, nil } diff --git a/transport/rest/auction_test.go b/transport/rest/auction_test.go index e543076..70225ca 100644 --- a/transport/rest/auction_test.go +++ b/transport/rest/auction_test.go @@ -25,8 +25,9 @@ var httpServer *rest.HTTPServer func runAuctionServer(t *testing.T) { repository := mocks.CreateAuctionMock() + mockBcClient := &mocks.MockBcClient{IsVerified: true} - service, err := service.New(repository) + service, err := service.New(repository, mockBcClient) if err != nil { log.Fatalf("error creating service: %v", err) }