Add Discover service implementation + some validators
This commit is contained in:
parent
bd524aff54
commit
27fb4f339e
|
@ -1,8 +1,16 @@
|
|||
{
|
||||
"extends": ["airbnb", "plugin:prettier/recommended"],
|
||||
"plugins": ["prettier"],
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error"
|
||||
"prettier/prettier": "error",
|
||||
"func-names": "off",
|
||||
"eqeqeq": "off",
|
||||
"class-methods-use-this": "off"
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
@ -12,4 +20,4 @@
|
|||
"parserOptions": {
|
||||
"ecmaVersion": 9
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,23 +2,23 @@ module.exports = {
|
|||
// applies to all environments
|
||||
default: {
|
||||
enabled: true,
|
||||
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
|
||||
rpcHost: 'localhost', // HTTP-RPC server listening interface (default: "localhost")
|
||||
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
|
||||
rpcCorsDomain: { // Domains from which to accept cross origin requests (browser enforced). This can also be a comma separated list
|
||||
auto: true, // When "auto" is true, Embark will automatically set the cors to the address of the webserver
|
||||
additionalCors: [] // Additional CORS domains to add to the list. If "auto" is false, only those will be added
|
||||
additionalCors: [], // Additional CORS domains to add to the list. If "auto" is false, only those will be added
|
||||
},
|
||||
wsRPC: true, // Enable the WS-RPC server
|
||||
wsOrigins: { // Same thing as "rpcCorsDomain", but for WS origins
|
||||
auto: true,
|
||||
additionalCors: []
|
||||
additionalCors: [],
|
||||
},
|
||||
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
|
||||
wsPort: 8546 // WS-RPC server listening port (default: 8546)
|
||||
wsHost: 'localhost', // WS-RPC server listening interface (default: "localhost")
|
||||
wsPort: 8546, // WS-RPC server listening port (default: 8546)
|
||||
|
||||
// Accounts to use as node accounts
|
||||
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
|
||||
/*,accounts: [
|
||||
/* ,accounts: [
|
||||
{
|
||||
nodeAccounts: true, // Accounts use for the node
|
||||
numAddresses: "1", // Number of addresses/accounts (defaults to 1)
|
||||
|
@ -39,68 +39,68 @@ module.exports = {
|
|||
numAddresses: "1", // Optional. The number of addresses to get
|
||||
hdpath: "m/44'/60'/0'/0/" // Optional. HD derivation path
|
||||
}
|
||||
]*/
|
||||
] */
|
||||
},
|
||||
|
||||
// default environment, merges with the settings in default
|
||||
// assumed to be the intended environment by `embark run` and `embark blockchain`
|
||||
development: {
|
||||
ethereumClientName: "geth", // Can be geth or parity (default:geth)
|
||||
//ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
|
||||
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
|
||||
ethereumClientName: 'geth', // Can be geth or parity (default:geth)
|
||||
// ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
|
||||
networkType: 'custom', // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
|
||||
networkId: 1337, // Network id used when networkType is custom
|
||||
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
|
||||
datadir: ".embark/development/datadir", // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
|
||||
datadir: '.embark/development/datadir', // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
|
||||
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
|
||||
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
|
||||
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
|
||||
proxy: true, // Proxy is used to present meaningful information about transactions
|
||||
targetGasLimit: 9000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
|
||||
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
|
||||
simulatorBlocktime: 0, // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run privatenet" and/or "embark blockchain privatenet"
|
||||
privatenet: {
|
||||
networkType: "custom",
|
||||
networkType: 'custom',
|
||||
networkId: 1337,
|
||||
isDev: false,
|
||||
datadir: ".embark/privatenet/datadir",
|
||||
datadir: '.embark/privatenet/datadir',
|
||||
// -- mineWhenNeeded --
|
||||
// This options is only valid when isDev is false.
|
||||
// This options is only valid when isDev is false.
|
||||
// Enabling this option uses our custom script to mine only when needed.
|
||||
// Embark creates a development account for you (using `geth account new`) and funds the account. This account can be used for
|
||||
// development (and even imported in to MetaMask). To enable correct usage, a password for this account must be specified
|
||||
// in the `account > password` setting below.
|
||||
// NOTE: once `mineWhenNeeded` is enabled, you must run an `embark reset` on your dApp before running
|
||||
// `embark blockchain` or `embark run` for the first time.
|
||||
mineWhenNeeded: true,
|
||||
mineWhenNeeded: true,
|
||||
// -- genesisBlock --
|
||||
// This option is only valid when mineWhenNeeded is true (which is only valid if isDev is false).
|
||||
// When enabled, geth uses POW to mine transactions as it would normally, instead of using POA as it does in --dev mode.
|
||||
// On the first `embark blockchain or embark run` after this option is enabled, geth will create a new chain with a
|
||||
// On the first `embark blockchain or embark run` after this option is enabled, geth will create a new chain with a
|
||||
// genesis block, which can be configured using the `genesisBlock` configuration option below.
|
||||
genesisBlock: "config/privatenet/genesis.json", // Genesis block to initiate on first creation of a development node
|
||||
genesisBlock: 'config/privatenet/genesis.json', // Genesis block to initiate on first creation of a development node
|
||||
nodiscover: true,
|
||||
maxpeers: 0,
|
||||
proxy: true,
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "config/privatenet/password" // Password to unlock the account
|
||||
}
|
||||
password: 'config/privatenet/password', // Password to unlock the account
|
||||
},
|
||||
],
|
||||
targetGasLimit: 8000000,
|
||||
simulatorBlocktime: 0
|
||||
simulatorBlocktime: 0,
|
||||
},
|
||||
|
||||
privateparitynet: {
|
||||
ethereumClientName: "parity",
|
||||
networkType: "custom",
|
||||
ethereumClientName: 'parity',
|
||||
networkType: 'custom',
|
||||
networkId: 1337,
|
||||
isDev: false,
|
||||
genesisBlock: "config/privatenet/genesis-parity.json", // Genesis block to initiate on first creation of a development node
|
||||
datadir: ".embark/privatenet/datadir",
|
||||
genesisBlock: 'config/privatenet/genesis-parity.json', // Genesis block to initiate on first creation of a development node
|
||||
datadir: '.embark/privatenet/datadir',
|
||||
mineWhenNeeded: false,
|
||||
nodiscover: true,
|
||||
maxpeers: 0,
|
||||
|
@ -108,43 +108,43 @@ module.exports = {
|
|||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "config/privatenet/password"
|
||||
}
|
||||
password: 'config/privatenet/password',
|
||||
},
|
||||
],
|
||||
targetGasLimit: 8000000,
|
||||
simulatorBlocktime: 0
|
||||
simulatorBlocktime: 0,
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run testnet" and/or "embark blockchain testnet"
|
||||
testnet: {
|
||||
networkType: "testnet",
|
||||
syncMode: "light",
|
||||
networkType: 'testnet',
|
||||
syncMode: 'light',
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "config/testnet/password"
|
||||
}
|
||||
]
|
||||
password: 'config/testnet/password',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// merges with the settings in default
|
||||
// used with "embark run livenet" and/or "embark blockchain livenet"
|
||||
livenet: {
|
||||
networkType: "livenet",
|
||||
syncMode: "light",
|
||||
rpcCorsDomain: "http://localhost:8000",
|
||||
wsOrigins: "http://localhost:8000",
|
||||
networkType: 'livenet',
|
||||
syncMode: 'light',
|
||||
rpcCorsDomain: 'http://localhost:8000',
|
||||
wsOrigins: 'http://localhost:8000',
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
password: "config/livenet/password"
|
||||
}
|
||||
]
|
||||
}
|
||||
password: 'config/livenet/password',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// you can name an environment with specific settings and then specify with
|
||||
// "embark run custom_name" or "embark blockchain custom_name"
|
||||
//custom_name: {
|
||||
//}
|
||||
// custom_name: {
|
||||
// }
|
||||
};
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "^7.4.3",
|
||||
"@trailofbits/embark-contract-info": "^1.0.0",
|
||||
"bignumber.js": "^8.1.1",
|
||||
"bs58": "^4.0.1",
|
||||
"connected-react-router": "^6.3.2",
|
||||
"debounce": "^1.2.0",
|
||||
"decimal.js": "^10.0.2",
|
||||
"embark": "^4.0.2",
|
||||
"embark-solium": "0.0.1",
|
||||
"history": "^4.7.2",
|
||||
"moment": "^2.24.0",
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const discoverValidatorUtils = {
|
||||
async checkDappCorrectness(dapp, id) {
|
||||
if (dapp.id != id) {
|
||||
throw new Error('Error fetching correct data')
|
||||
}
|
||||
},
|
||||
async checkUpVotingAmount(amount, limit) {
|
||||
if (amount > limit) {
|
||||
throw new Error('You cannot upvote by this much, try with a lower amount')
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default discoverValidatorUtils
|
|
@ -0,0 +1,43 @@
|
|||
import DiscoverContract from '../../../../embarkArtifacts/contracts/Discover'
|
||||
import DiscoverRServiceValidator from './validator'
|
||||
|
||||
class DiscoverReadService {
|
||||
constructor() {
|
||||
this.validator = new DiscoverRServiceValidator(this)
|
||||
}
|
||||
|
||||
// TODO: Amount -> string/bigInt/number ?
|
||||
// TODO: Maybe we can get id from a DApp name ?
|
||||
// TODO: formatBigNumberToNumber
|
||||
// TODO: validators - YUP
|
||||
async upVoteEffect(id, amount) {
|
||||
const dapp = await this.getDAppById(id)
|
||||
await this.validator.validateUpVoteEffect(dapp, id, amount)
|
||||
|
||||
return DiscoverContract.methods.upvoteEffect(id, amount).call()
|
||||
}
|
||||
|
||||
async downVoteCost(id) {
|
||||
const dapp = await this.getDAppById(id)
|
||||
await this.validator.validateDownVoteCost(dapp, id)
|
||||
|
||||
return DiscoverContract.methods.upvoteEffect(id).call()
|
||||
}
|
||||
|
||||
async getDAppById(id) {
|
||||
const dappId = await DiscoverContract.methods.id2index(id).call()
|
||||
return DiscoverContract.methods.dapps(dappId).call()
|
||||
}
|
||||
|
||||
async safeMax() {
|
||||
console.log(DiscoverContract)
|
||||
debugger
|
||||
return DiscoverContract.safeMax()
|
||||
}
|
||||
|
||||
async isDAppExists(id) {
|
||||
return DiscoverContract.methods.existingIDs(id).call()
|
||||
}
|
||||
}
|
||||
|
||||
export default DiscoverReadService
|
|
@ -0,0 +1,26 @@
|
|||
import DiscoverValidatorUtils from '../discover-validator-utils'
|
||||
|
||||
class DiscoverReadServiceValidator {
|
||||
constructor(service) {
|
||||
this.service = service
|
||||
}
|
||||
|
||||
async validateUpVoteEffect(id, amount) {
|
||||
const dapp = await this.service.getDAppById()
|
||||
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
|
||||
|
||||
// TODO: should check if dapp.balance is a big number
|
||||
const safeMax = await this.service.safeMax()
|
||||
await DiscoverValidatorUtils.checkUpVotingAmount(
|
||||
dapp.balance + amount,
|
||||
safeMax,
|
||||
)
|
||||
}
|
||||
|
||||
async validateDownVoteCost(id) {
|
||||
const dapp = await this.service.getDAppById()
|
||||
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
|
||||
}
|
||||
}
|
||||
|
||||
export default DiscoverReadServiceValidator
|
|
@ -0,0 +1,84 @@
|
|||
import DiscoverContract from '../../../../embarkArtifacts/contracts/Discover'
|
||||
import DiscoverRService from '../read-service/discover-r-service'
|
||||
|
||||
import DiscoverWServiceValidator from './validator'
|
||||
|
||||
// TODO: Validators ?
|
||||
// TODO: check for unlocked account: If it is not -> request unlocking
|
||||
// TODO: preOperation -> inherited method ?
|
||||
const unlockAccount = async function(account) {
|
||||
return account
|
||||
}
|
||||
|
||||
class DiscoverWriteService extends DiscoverRService {
|
||||
constructor(unlockedAccount) {
|
||||
this.account = unlockedAccount
|
||||
this.validator = new DiscoverWServiceValidator(this)
|
||||
}
|
||||
|
||||
async createDApp(id, amount, metadata) {
|
||||
await unlockAccount(this.account)
|
||||
await this.validator.validateDAppCreation(id, amount)
|
||||
|
||||
try {
|
||||
await DiscoverContract.methods.createDApp(id, amount, metadata, {
|
||||
from: this.account,
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error('Transfer failed')
|
||||
}
|
||||
}
|
||||
|
||||
async upVote(id, amount) {
|
||||
await unlockAccount(this.account)
|
||||
await this.validator.validateUpVoting(id, amount)
|
||||
|
||||
try {
|
||||
await DiscoverContract.methods.upVote(id, amount, { from: this.account })
|
||||
} catch (error) {
|
||||
throw new Error('Transfer failed')
|
||||
}
|
||||
}
|
||||
|
||||
async downVote(id, amount) {
|
||||
await unlockAccount(this.account)
|
||||
await this.validator.validateDownVoting(id, amount)
|
||||
|
||||
try {
|
||||
await DiscoverContract.methods.downVote(id, amount, {
|
||||
from: this.account,
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error('Transfer failed')
|
||||
}
|
||||
}
|
||||
|
||||
async withdraw(id, amount) {
|
||||
await unlockAccount(this.account)
|
||||
await this.validator.validateWithdrawing(id, amount)
|
||||
|
||||
try {
|
||||
await DiscoverContract.methods.withdraw(id, amount, this.account)
|
||||
} catch (error) {
|
||||
throw new Error('Transfer failed')
|
||||
}
|
||||
}
|
||||
|
||||
async setMetadata(id, metadata) {
|
||||
await unlockAccount(this.account)
|
||||
await this.validator.validateMetadataSet(id)
|
||||
|
||||
await DiscoverContract.methods.setMetadata(id, metadata, {
|
||||
from: this.account,
|
||||
})
|
||||
}
|
||||
|
||||
// async receiveApproval(from, amount, token, data) {
|
||||
// await unlockAccount(this.account);
|
||||
// await this.validator.validateReceiveApproval();
|
||||
|
||||
// await DiscoverContract.methods.receiveApproval(from, amount, token, data, { from: this.account });
|
||||
// }
|
||||
}
|
||||
|
||||
export default DiscoverWriteService
|
|
@ -0,0 +1,71 @@
|
|||
import DiscoverValidatorUtils from '../discover-validator-utils'
|
||||
import DiscoverRServiceValidator from '../read-service/validator'
|
||||
|
||||
class DiscoverWriteServiceValidator extends DiscoverRServiceValidator {
|
||||
// TODO: Add SNT allowance checks
|
||||
async validateDAppCreation(id, amount) {
|
||||
const dappExists = await this.service.isDAppExists(id)
|
||||
if (dappExists) {
|
||||
throw new Error('You must submit a unique ID')
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
throw new Error(
|
||||
'You must spend some SNT to submit a ranking in order to avoid spam',
|
||||
)
|
||||
}
|
||||
|
||||
const safeMax = await this.service.safeMax()
|
||||
if (amount > safeMax) {
|
||||
throw new Error('You cannot stake more SNT than the ceiling dictates')
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add SNT allowance checks
|
||||
async validateUpVoting(id, amount) {
|
||||
await super.validateUpVoteEffect(id, amount)
|
||||
|
||||
if (amount <= 0) {
|
||||
throw new Error('You must send some SNT in order to upvote')
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add SNT allowance checks
|
||||
async validateDownVoting(id, amount) {
|
||||
await super.validateDownVoteCost(id)
|
||||
|
||||
const downVoteCost = await this.service.downVoteCost(id)
|
||||
if (downVoteCost != amount) {
|
||||
throw new Error('Incorrect amount: valid iff effect on ranking is 1%')
|
||||
}
|
||||
}
|
||||
|
||||
async validateWithdrawing(id, amount) {
|
||||
const dapp = await this.service.getDAppById(id)
|
||||
await DiscoverValidatorUtils.checkDappCorrectness(dapp, id)
|
||||
|
||||
if (dapp.developer != this.service.account) {
|
||||
throw new Error('Only the developer can withdraw SNT staked on this data')
|
||||
}
|
||||
|
||||
if (amount > dapp.available) {
|
||||
throw new Error(
|
||||
'You can only withdraw a percentage of the SNT staked, less what you have already received',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async validateMetadataSet(id) {
|
||||
const dapp = await this.service.getDAppById(id)
|
||||
|
||||
if (dapp.developer != this.service.account) {
|
||||
throw new Error('Only the developer can update the metadata')
|
||||
}
|
||||
}
|
||||
|
||||
// async validateReceiveApproval() {
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
export default DiscoverWriteServiceValidator
|
|
@ -0,0 +1,25 @@
|
|||
// import DiscoverContract from '../../../embarkArtifacts/contracts/Discover';
|
||||
import EmbarkJS from '../../embarkArtifacts/embarkjs'
|
||||
|
||||
import DiscoverReadService from './discover-services/read-service/discover-r-service'
|
||||
import DiscoverWriteService from './discover-services/write-service/discover-w-service'
|
||||
|
||||
const ReadOnlyServices = {
|
||||
DiscoverService: new DiscoverReadService(),
|
||||
}
|
||||
|
||||
// TODO: ask Andy what kind of wallets is going to be used
|
||||
const init = async function() {
|
||||
try {
|
||||
const account = await EmbarkJS.enableEthereum()
|
||||
|
||||
const discoverService = new DiscoverWriteService(account)
|
||||
|
||||
return { DiscoverService: discoverService, ...ReadOnlyServices }
|
||||
} catch (error) {
|
||||
// TODO: Should handle it in an elegant way
|
||||
throw new Error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export default { init, ...ReadOnlyServices }
|
|
@ -0,0 +1,29 @@
|
|||
const ONE = '1000000000000000000'
|
||||
|
||||
const formatBigNumberToNumber = function(bigNumber) {
|
||||
let stringifyedNumber = bigNumber.toString(10)
|
||||
|
||||
if (stringifyedNumber == '0') {
|
||||
return stringifyedNumber
|
||||
}
|
||||
|
||||
let numberWholePartLength = 0
|
||||
|
||||
if (bigNumber.lt(ONE)) {
|
||||
stringifyedNumber = stringifyedNumber.padStart(19, 0)
|
||||
numberWholePartLength = 1
|
||||
} else {
|
||||
numberWholePartLength = bigNumber.div('1000000000000000000').toString(10)
|
||||
.length
|
||||
}
|
||||
|
||||
return `${stringifyedNumber.substr(
|
||||
0,
|
||||
numberWholePartLength,
|
||||
)}.${stringifyedNumber.substr(
|
||||
numberWholePartLength,
|
||||
stringifyedNumber.length,
|
||||
)}`
|
||||
}
|
||||
|
||||
export default formatBigNumberToNumber
|
|
@ -5,6 +5,7 @@ import Filtered from '../Filtered'
|
|||
import RecentlyAdded from '../RecentlyAdded'
|
||||
import Vote from '../Vote'
|
||||
import Dapps from '../Dapps'
|
||||
import Example from '../BlockchainExample'
|
||||
|
||||
export default () => (
|
||||
<Switch>
|
||||
|
@ -13,5 +14,6 @@ export default () => (
|
|||
<Route path="/all" component={Dapps} />
|
||||
<Route path="/recently-added" component={RecentlyAdded} />
|
||||
<Route path="/vote" component={Vote} />
|
||||
<Route path="/example" component={Example} />
|
||||
</Switch>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import { connect } from 'react-redux'
|
||||
import BlockchainExample from './BlockchainExample'
|
||||
|
||||
export default connect()(BlockchainExample)
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import BlockchainSDK from '../../common/blockchain'
|
||||
// import EmbarkJS from '../../embarkArtifacts/embarkjs';
|
||||
|
||||
class Example extends React.Component {
|
||||
async logDiscoverMethod() {
|
||||
console.log('here')
|
||||
debugger
|
||||
console.log(await BlockchainSDK.DiscoverService.safeMax())
|
||||
}
|
||||
|
||||
render() {
|
||||
return <h1 onLoad={this.logDiscoverMethod()} />
|
||||
}
|
||||
}
|
||||
|
||||
export default Example
|
|
@ -0,0 +1,3 @@
|
|||
import BlockchainExample from './BlockchainExample.container'
|
||||
|
||||
export default BlockchainExample
|
Loading…
Reference in New Issue