Browse Source

Inital commit

master
Slixe 3 years ago
commit
91f6e3792d
33 changed files with 10205 additions and 0 deletions
  1. +73
    -0
      build.gradle
  2. +29
    -0
      dero-stats/README.md
  3. +5
    -0
      dero-stats/babel.config.js
  4. +43
    -0
      dero-stats/package.json
  5. +18
    -0
      dero-stats/public/index.html
  6. BIN
      dero-stats/public/logo.png
  7. +74
    -0
      dero-stats/src/App.vue
  8. +37
    -0
      dero-stats/src/main.js
  9. +218
    -0
      dero-stats/src/views/Main.vue
  10. +8317
    -0
      dero-stats/yarn.lock
  11. BIN
      gradle/wrapper/gradle-wrapper.jar
  12. +5
    -0
      gradle/wrapper/gradle-wrapper.properties
  13. +188
    -0
      gradlew
  14. +100
    -0
      gradlew.bat
  15. +10
    -0
      settings.gradle
  16. +57
    -0
      src/main/java/fr/slixe/mining/App.java
  17. +12
    -0
      src/main/java/fr/slixe/mining/JsonIgnore.java
  18. +61
    -0
      src/main/java/fr/slixe/mining/Main.java
  19. +26
    -0
      src/main/java/fr/slixe/mining/MyModule.java
  20. +11
    -0
      src/main/java/fr/slixe/mining/http/InvalidParameterException.java
  21. +188
    -0
      src/main/java/fr/slixe/mining/http/SparkHttpServer.java
  22. +91
    -0
      src/main/java/fr/slixe/mining/http/controller/MainController.groovy
  23. +154
    -0
      src/main/java/fr/slixe/mining/service/DaemonService.java
  24. +21
    -0
      src/main/java/fr/slixe/mining/service/SessionManager.java
  25. +48
    -0
      src/main/java/fr/slixe/mining/service/StatsService.java
  26. +79
    -0
      src/main/java/fr/slixe/mining/structures/BlockTemplate.java
  27. +16
    -0
      src/main/java/fr/slixe/mining/structures/FakeRPCResult.java
  28. +140
    -0
      src/main/java/fr/slixe/mining/structures/Info.java
  29. +81
    -0
      src/main/java/fr/slixe/mining/structures/Miner.java
  30. +52
    -0
      src/main/java/fr/slixe/mining/structures/SubmitBlock.java
  31. +10
    -0
      src/main/resources/config.default.json
  32. +29
    -0
      src/main/resources/config/app.config.groovy
  33. +12
    -0
      src/main/resources/config/routes.groovy

+ 73
- 0
build.gradle View File

@@ -0,0 +1,73 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/5.4/userguide/java_library_plugin.html
*/

plugins {
id 'java-library'
id 'groovy'
id 'eclipse'
id 'application'
id 'idea'
}

// Groovy interop
sourceSets.main.java.srcDirs = []
sourceSets.main.groovy.srcDirs += ["src/main/java"]

compileJava.options.encoding = 'UTF-8'

mainClassName = 'fr.slixe.mining.Main'

group 'fr.slixe'
version '1.0.0'

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
maven {
url 'http://wytrem.github.io/maven'
}

maven {
url 'https://paladin-framework.github.io/maven'
}

mavenCentral()
}

dependencies {
implementation(
'com.sparkjava:spark-core:2.8.0',
'net.sf.trove4j:trove4j:3.0.3',
'com.google.code.gson:gson:2.8.5',
'com.mashape.unirest:unirest-java:1.4.9',
'org.apache.commons:commons-lang3:3.8.1',
'org.apache.logging.log4j:log4j-core:2.10.0',
)

implementation('fr.litarvan.paladin:paladin-framework:1.2.0') {
exclude group: 'org.apache.httpcomponents', module: 'httpcore-nio'
exclude group: 'org.apache.logging.log4j', module: 'log4j-core'
}
}

task fatJar(type: Jar) {
from {
configurations
.runtimeClasspath
.findAll { !it.name.endsWith('pom') }
.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar

baseName = 'dero-mining'

manifest {
attributes 'Main-Class': mainClassName
}
}

+ 29
- 0
dero-stats/README.md View File

@@ -0,0 +1,29 @@
# dero-stats

## Project setup
```
yarn install
```

### Compiles and hot-reloads for development
```
yarn run serve
```

### Compiles and minifies for production
```
yarn run build
```

### Run your tests
```
yarn run test
```

### Lints and fixes files
```
yarn run lint
```

### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5
- 0
dero-stats/babel.config.js View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

+ 43
- 0
dero-stats/package.json View File

@@ -0,0 +1,43 @@
{
"name": "dero-stats",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.4.4",
"material-design-icons": "^3.0.1",
"vue": "^2.6.10",
"vuetify": "^2.2.4"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"babel-eslint": "^10.0.3",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

+ 18
- 0
dero-stats/public/index.html View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>logo.png">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700%7CMaterial+Icons' rel="stylesheet">
<title>DERO Stats</title>
</head>
<body>
<noscript>
<strong>We're sorry but DERO Stats doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

BIN
dero-stats/public/logo.png View File

Before After
Width: 741  |  Height: 741  |  Size: 99KB

+ 74
- 0
dero-stats/src/App.vue View File

@@ -0,0 +1,74 @@
<template>
<div id="app">
<v-app>
<header>
<v-toolbar color="secondary">
<v-avatar size="60" tile>
<v-img src="/logo.png">
</v-img>
</v-avatar>
<v-btn to="/" text>
<v-toolbar-title>{{ $name }}</v-toolbar-title>
</v-btn>
<v-toolbar-items>
<v-btn @click="changeTheme()" text><span><v-icon>invert_colors</v-icon></span></v-btn>
</v-toolbar-items>
</v-toolbar>
</header>
<v-main>
<Main></Main>
</v-main>
<v-footer elevation="10" padless>
<v-card flat tile width="100%" class="text-center" color="primary">
<v-card-text v-if="$donations">
dERokevAZEZVJ2N7o39VH81BXBqX9ojtncnPTDMyiVbmYiTXQY93AUCLcor9xsWCKWhYy25ja89ikZWXWab9kXRB7LYfUmbQyS
</v-card-text>
<v-divider></v-divider>
<v-card-text>
{{ new Date().getFullYear() }} — <strong>{{ $name }}</strong>
</v-card-text>
</v-card>
</v-footer>
</v-app>
</div>
</template>

<script>
import Main from './views/Main'

export default {
name: 'app',
components: {
Main
},
mounted() {
if (localStorage.theme) {
this.$vuetify.theme.dark = localStorage.theme == "dark"
} else {
this.$vuetify.theme.dark = true
}
},
methods: {
changeTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
if (this.$vuetify.theme.dark) {
localStorage.theme = "dark"
} else {
localStorage.theme = "light"
}
}
}
}
</script>

<style>
#app {
color: white;
text-align: center;
background: var(--v-anchor-base);
}

* {
transition: background-color 200ms ease, color 150ms ease-in-out;
}
</style>

+ 37
- 0
dero-stats/src/main.js View File

@@ -0,0 +1,37 @@
import Vue from 'vue'
import App from './App.vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'

Vue.config.productionTip = false

Vue.prototype.$name = "DERO Mining Stats"
Vue.prototype.$api = "http://localhost:8080/api"
Vue.prototype.$donations = false
Vue.use(Vuetify)
new Vue({
render: h => h(App),
vuetify: new Vuetify({
icons: {
iconfont: 'md',
},
theme: {
options: {
customProperties: true,
},
themes: {
dark: {
primary: '#383B3E',
secondary: '#2A2D2F',
anchor: '#303030',
},
light: {
primary: '#DCDCDC',
secondary: '#DCDCDC',
anchor: '#B0C4DE',
}
},
dark: true
},
}),
}).$mount('#app')

+ 218
- 0
dero-stats/src/views/Main.vue View File

@@ -0,0 +1,218 @@
<template>
<div id="main">
<div class="first-line">
<v-card class="elevation-5 infos" color="primary">
<v-card-title class="center-text">
<h2>Daemon Infos</h2>
</v-card-title>
<v-card-text>
<ul>
<li><strong>Current Height:</strong> {{ formatValue(infos.height) }}</li>
<li><strong>Topo Height:</strong> {{ formatValue(infos.topoheight) }}</li>
<li><strong>Hashrate:</strong> {{ formatValue((infos.difficulty/(infos.target*1000*1000)).toFixed(2)) }} MH/s</li>
<li><strong>Average Block Time:</strong> {{ infos.averageblocktime50 }}s</li>
<li><strong>Difficulty:</strong> {{ formatValue(infos.difficulty) }} </li>
<li><strong>Median Block Size:</strong> {{ formatValue(infos.median_block_size/1000) }} kB</li>
<li><strong>Current Supply:</strong> {{ formatValue(infos.total_supply) }} DERO</li>
</ul>
</v-card-text>
</v-card>
<v-card class="elevation-5 blocks" :loading="loading" color="primary">
<v-card-title>
<h2>Blocks Found</h2>
<v-spacer></v-spacer>
<v-text-field class="search" v-model="blocksSearch" append-icon="magnify" label="Search" single-line hide-details></v-text-field>
</v-card-title>
<v-data-table v-model="selected" show-select no-data-text="No blocks found" :search="blocksSearch" multi-sort :headers="blocksHeaders" :items="blocks" :items-per-page="5">
</v-data-table>
</v-card>
</div>
<v-card class="elevation-5 miners" :loading="loading" color="primary">
<v-card-title>
<h2>Connected Miners</h2>
<v-spacer></v-spacer>
<v-text-field class="search" v-model="minersSearch" no-data-text="No miners connected" append-icon="magnify" label="Search" single-line hide-details></v-text-field>
</v-card-title>
<v-data-table v-model="selected" show-select :search="minersSearch" multi-sort :headers="headers" :items="miners" :items-per-page="5">
<template v-slot:item.lastJobRequest="{ item }">
<span>{{ formatTime(item.lastJobRequest) }}</span>
</template>
<template v-slot:item.lastBlockSent="{ item }">
<span>{{ item.lastBlockSent != -1 ? formatTime(item.lastBlockSent) : "No block found yet" }}</span>
</template>
<!--<template v-slot:item.alive="{ item }">
<span :style="'color:' + (item.alive ? 'green' : 'red')">{{ item.alive }}</span>
</template>-->
</v-data-table>
<v-card-text v-show="!this.loading" style="text-align: center;"><strong>Currently mining on:</strong> {{ infos.walletAddress }}</v-card-text>
</v-card>
</div>
</template>

<script>
export default {
data() {
return {
loading: true,
minersSearch: "",
blocksSearch: "",
selected: [],
headers: [
{
text: "IP",
align: "start",
value: "ip"
},
{
text: "Last Job Request",
value: "lastJobRequest"
},
{
text: "Last Block Found",
value: "lastBlockSent"
},
{
text: "Blocks Found",
value: "blocksFound"
},
{
text: "Connected",
value: "alive"
}
],
blocksHeaders: [
{
text: "Miner",
align: "start",
value: "minerIp"
},
{
text: "Block Height",
value: "height"
},
{
text: "Hash",
value: "blid"
},
{
text: "Status",
value: "status"
}
],
miners: [],
infos: {},
blocks: []
}
},
mounted() {
this.update()
setInterval(() => this.update(), 5000)
},
methods: {
update() {
fetch(this.$api + "/info").then(result => result.json()).then(json => {
this.infos = json
})

fetch(this.$api + "/blocks").then(result => result.json()).then(json => {
json.reverse()
this.blocks = json
})

fetch(this.$api + "/miners").then(result => result.json()).then(json => {
if (this.loading) {
this.loading = false
}
this.miners = json
})
},
formatTime(timestamp) {
let date = new Date(timestamp);
return ("00" + (date.getMonth() + 1)).slice(-2) + "/" +
("00" + date.getDate()).slice(-2) + "/" +
date.getFullYear() + " " +
("00" + date.getHours()).slice(-2) + ":" +
("00" + date.getMinutes()).slice(-2) + ":" +
("00" + date.getSeconds()).slice(-2);
},
formatValue(value) {
if (!value) return 0
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ")
}
}
}
</script>

<style scoped>
#main {
margin: 15%;
margin-top: 5%;
margin-bottom: 5%;
}
.theme--dark.v-data-table {
background-color: var(--v-primary-base);
}

.theme--light.v-data-table {
background-color: var(--v-primary-base);
}

.first-line {
display: flex;
justify-items: center;
align-items: flex-start;
}

.infos {
width: 30%;
text-align: left;
}

ul li {
list-style: none;
padding: 0;
margin-bottom: 2%;
}

.blocks {
margin: auto;
margin-left: 5%;
width: 100%;
padding-left: 1%;
padding-right: 1%;
}

.miners {
margin-top: 5%;
padding-left: 1%;
padding-right: 1%;
width: 100%;
}

.center-text {
text-align: center;
}

@media screen and (max-width: 1280px)
{
#main {
margin: 5%;
}

.first-line {
margin: auto;
flex-direction: column;
}

.infos {
margin: auto;
width: auto;
}

.blocks {
margin: auto;
margin-top: 5%;
margin-bottom: 5%;
}
}
</style>

+ 8317
- 0
dero-stats/yarn.lock
File diff suppressed because it is too large
View File


BIN
gradle/wrapper/gradle-wrapper.jar View File


+ 5
- 0
gradle/wrapper/gradle-wrapper.properties View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

+ 188
- 0
gradlew View File

@@ -0,0 +1,188 @@
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
echo "$*"
}

die () {
echo
echo "$*"
echo
exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option

if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"

+ 100
- 0
gradlew.bat View File

@@ -0,0 +1,100 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

+ 10
- 0
settings.gradle View File

@@ -0,0 +1,10 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/5.4/userguide/multi_project_builds.html
*/

rootProject.name = 'dero-mining'

+ 57
- 0
src/main/java/fr/slixe/mining/App.java View File

@@ -0,0 +1,57 @@
package fr.slixe.mining;
import java.util.Timer;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import fr.litarvan.paladin.OnStart;
import fr.litarvan.paladin.OnStop;
import fr.litarvan.paladin.PaladinApp;
import fr.litarvan.paladin.PaladinConfig;
import fr.slixe.mining.service.DaemonService;
import spark.Spark;
@PaladinApp(name = "DERO Mining", version = App.VERSION, author = "Slixe")
public class App
{
public static final String VERSION = "1.0.0";
private static final Logger log = LoggerFactory.getLogger("DERO Mining");
private final Timer timer = new Timer();
@Inject
private DaemonService daemon;
@Inject
private PaladinConfig config;
@OnStart
public void start()
{
TimerTask task = new TimerTask() {
@Override
public void run()
{
daemon.updateInfo();
daemon.updateJob();
}
};
this.timer.schedule(task, 0, config.get("job-interval", long.class));
}
@OnStop
public void stop()
{
this.timer.cancel();
log.info("Shutting down http service...");
Spark.stop();
}
}

+ 12
- 0
src/main/java/fr/slixe/mining/JsonIgnore.java View File

@@ -0,0 +1,12 @@
package fr.slixe.mining;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonIgnore
{
}

+ 61
- 0
src/main/java/fr/slixe/mining/Main.java View File

@@ -0,0 +1,61 @@
package fr.slixe.mining;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.litarvan.paladin.Paladin;
import fr.litarvan.paladin.PaladinBuilder;
import fr.litarvan.paladin.PaladinConfig;
import fr.slixe.mining.http.SparkHttpServer;
import fr.slixe.mining.service.SessionManager;
public class Main
{
private static final Logger log = LoggerFactory.getLogger("Main");
public static void main(String[] args)
{
Paladin paladin = PaladinBuilder.create(App.class)
.addModule(new MyModule())
.loadCommandLineArguments(args)
.setSessionManager(new SessionManager())
.build();
PaladinConfig config = paladin.getConfig();
SparkHttpServer httpServer = new SparkHttpServer(paladin, config.get("port", int.class));
if (config.get("enableSSL", boolean.class))
{
log.info("Enabling SSL...");
String filePath = config.get("keystoreFile");
char[] secret = config.get("keystorePassword").toCharArray();
File file = new File(filePath);
if (!file.exists()) {
log.error(String.format("Keystore file at '%s' not found. Skipping SSL...", file.getAbsolutePath()));
}
else {
try {
httpServer.loadSSLCert(file, secret);
log.info("SSL is now enabled!");
} catch (GeneralSecurityException | IOException e) {
log.error(e.getLocalizedMessage());
}
}
}
log.info(config.get("walletAddress"));
if ("dERokevAZEZVJ2N7o39VH81BXBqX9ojtncnPTDMyiVbmYiTXQY93AUCLcor9xsWCKWhYy25ja89ikZWXWab9kXRB7LYfUmbQyS".contentEquals(config.get("walletAddress")))
{
log.warn("You're running using the default DERO wallet address!");
log.warn("If you want to change it: edit the 'config.json' file.");
}
paladin.start(httpServer);
}
}

+ 26
- 0
src/main/java/fr/slixe/mining/MyModule.java View File

@@ -0,0 +1,26 @@
package fr.slixe.mining;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.AbstractModule;
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Gson.class).toInstance(
new GsonBuilder().setPrettyPrinting().addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(JsonIgnore.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}).create());
}
}

+ 11
- 0
src/main/java/fr/slixe/mining/http/InvalidParameterException.java View File

@@ -0,0 +1,11 @@
package fr.slixe.mining.http;
import fr.litarvan.paladin.http.routing.RequestException;
public class InvalidParameterException extends RequestException
{
public InvalidParameterException(String message)
{
super("Invalid parameter : " + message);
}
}

+ 188
- 0
src/main/java/fr/slixe/mining/http/SparkHttpServer.java View File

@@ -0,0 +1,188 @@
package fr.slixe.mining.http;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import fr.litarvan.paladin.Paladin;
import fr.litarvan.paladin.http.Cookie;
import fr.litarvan.paladin.http.Header;
import fr.litarvan.paladin.http.HeaderPair;
import fr.litarvan.paladin.http.HttpMethod;
import fr.litarvan.paladin.http.Request;
import fr.litarvan.paladin.http.Response;
import fr.litarvan.paladin.http.server.PaladinHttpServer;
import spark.Spark;
public class SparkHttpServer implements PaladinHttpServer
{
private Paladin paladin;
private int port;
private boolean isRunning;
public SparkHttpServer(Paladin paladin, int port)
{
this.paladin = paladin;
this.port = port;
}
@Override
public void start() throws IOException
{
Spark.port(port);
Spark.threadPool(15);
Spark.staticFileLocation("public");
Spark.before((request, originalResponse) -> {
Response response = new Response();
paladin.execute(createRequest(request, response), response);
applyResponse(originalResponse, response);
Spark.halt();
});
Spark.awaitInitialization();
this.isRunning = true;
}
protected Request createRequest(spark.Request request, Response response)
{
String[] headerNames = request.headers().toArray(new String[0]);
Header[] headers = new Header[headerNames.length];
for (int i = 0; i < headerNames.length; i++) {
headers[i] = new Header(headerNames[i], request.headers(headerNames[i]));
}
String[] queryParams = request.queryParams().toArray(new String[0]);
Map<String, String> params = new HashMap<>(request.params());
for (String queryParam : queryParams) {
params.put(queryParam, request.queryParams(queryParam));
}
String[] cookieNames = request.cookies().keySet().toArray(new String[0]);
Cookie[] cookies = new Cookie[cookieNames.length];
for (int i = 0; i < cookieNames.length; i++) {
cookies[i] = new Cookie(cookieNames[i], request.cookie(cookieNames[i]));
}
Header contentType = null;
for (Header header : headers)
{
if (header.getName().equalsIgnoreCase(Header.CONTENT_TYPE))
{
contentType = header;
break;
}
}
if (contentType != null && Header.CONTENT_TYPE_FORM_URL_ENCODED.equals(contentType.getValue()))
{
extractParams(params, new String(request.bodyAsBytes(), Charset.defaultCharset()));
}
return new Request(paladin, request.ip(), HttpMethod.valueOf(request.requestMethod()), request.uri(), headers, request.bodyAsBytes(), params, cookies, response);
}
protected void extractParams(Map<String, String> params, String query)
{
for (String param : query.split("&"))
{
String[] split = param.split("=");
try
{
params.put(URLDecoder.decode(split[0], Charset.defaultCharset().name()), split.length > 1 ? URLDecoder.decode(split[1], Charset.defaultCharset().name()) : "");
}
catch (UnsupportedEncodingException e)
{
// Can't happen
}
}
}
protected void applyResponse(spark.Response sparkResponse, Response response)
{
for (Header header : response.getHeaders()) {
String value = "";
if (header.getValue() != null && !header.getValue().isEmpty())
{
value = header.getValue();
}
else if (header.getPairs() != null)
{
for (int i1 = 0; i1 < header.getPairs().size(); i1++)
{
HeaderPair pair = header.getPairs().get(i1);
try
{
value += pair.getName() + "=" + URLEncoder.encode(pair.getValue(), Charset.defaultCharset().name()) + (i1 + 1 < header.getPairs().size() ? "; " : "");
}
catch (UnsupportedEncodingException ignored)
{
// Can't happen
}
}
}
sparkResponse.header(header.getName(), value);
}
for (Cookie cookie : response.getCookies()) {
// TODO: Cookie params ? (& Dans Paladin)
sparkResponse.cookie(cookie.getName(), cookie.getValue());
}
sparkResponse.status(response.getCode());
try {
sparkResponse.raw().getOutputStream().write(response.getContent());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void loadSSLCert(File file, char[] secret) throws GeneralSecurityException, IOException
{
Spark.secure(file.getPath(), new String(secret), null, null);
}
@Override
public synchronized void waitFor() throws InterruptedException
{
while (this.isRunning) {
this.wait();
}
}
@Override
public void shutdown()
{
this.isRunning = false;
Spark.stop();
}
@Override
public String getAddress()
{
try {
return InetAddress.getLocalHost().getHostAddress() + ":" + port;
} catch (UnknownHostException e) {
return ":" + port;
}
}
}

+ 91
- 0
src/main/java/fr/slixe/mining/http/controller/MainController.groovy View File

@@ -0,0 +1,91 @@
package fr.slixe.mining.http.controller;

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import com.google.gson.Gson;
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.inject.Inject;

import fr.litarvan.paladin.http.Controller;
import fr.litarvan.paladin.http.Request
import fr.litarvan.paladin.http.routing.JsonBody
import fr.slixe.mining.http.InvalidParameterException
import fr.slixe.mining.service.DaemonService
import fr.slixe.mining.service.StatsService
import fr.slixe.mining.structures.FakeRPCResult
import fr.slixe.mining.structures.Miner

public class MainController extends Controller {

private static final Logger log = LoggerFactory.getLogger("Controller");

@Inject
private Gson gson

@Inject
private DaemonService daemon

@Inject
private StatsService stats


@JsonBody(parse = false)
def jsonRpc(Request request)
{
String content = request.getParam("__jBdy");
JsonElement jsonElement = new JsonParser().parse(content)

if (jsonElement == null || !jsonElement.isJsonObject()) {
throw new InvalidParameterException("Invalid body")
}

JsonObject json = jsonElement.getAsJsonObject()
String method = json.get("method").getAsString()
Optional<Miner> optMiner = stats.findMiner(request.ip)
Miner miner = optMiner.isPresent() ? optMiner.get() : stats.createMiner(request.ip)
miner.markAlive()

switch (method) {
case "get_info":
return new FakeRPCResult(daemon.info)
case "getblocktemplate":
miner.lastJobRequest = System.currentTimeMillis()
return new FakeRPCResult(daemon.blockTemplate)
case "submitblock":
miner.lastBlockSent = System.currentTimeMillis()
def jsonArray = json.get("params").getAsJsonArray()
def x = new String[2];
x[0] = jsonArray.get(0).getAsString();
x[1] = jsonArray.get(1).getAsString();
return new FakeRPCResult(daemon.submitBlock(miner, x))
}

[
message: "Invalid method"
]
}

def job()
{
daemon.blockTemplate
}

def info()
{
daemon.info
}

def miners()
{
stats.miners
}

def blocks()
{
stats.blocks
}
}

+ 154
- 0
src/main/java/fr/slixe/mining/service/DaemonService.java View File

@@ -0,0 +1,154 @@
package fr.slixe.mining.service;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.mashape.unirest.request.body.RequestBodyEntity;

import fr.litarvan.paladin.PaladinConfig;
import fr.slixe.mining.structures.BlockTemplate;
import fr.slixe.mining.structures.Info;
import fr.slixe.mining.structures.Miner;
import fr.slixe.mining.structures.SubmitBlock;

@Singleton
public class DaemonService {

private static final Logger log = LoggerFactory.getLogger("Daemon Service");

private final String daemonURL;
private final RequestBodyEntity requestJob;
private final RequestBodyEntity requestInfo;
private final String walletAddress;

private BlockTemplate blockTemplate;
private Info info;

@Inject
private Gson gson;

@Inject
private StatsService stats;

@Inject
public DaemonService(PaladinConfig config)
{
this.daemonURL = config.get("daemonURL") + "/json_rpc";

this.walletAddress = config.get("walletAddress");
int reserveSize = config.get("reserveSize", int.class);

final Map<String, Object> params = new HashMap<>();
params.put("wallet_address", walletAddress);
params.put("reserve_size", reserveSize);

JSONObject body = body("getblocktemplate", params);
this.requestJob = Unirest.post(daemonURL).header("Content-Type", "application/json").body(body);

body = body("get_info");
this.requestInfo = Unirest.post(daemonURL).header("Content-Type", "application/json").body(body);
}

public void updateInfo()
{
Info info = call(requestInfo, Info.class);
if (info != null) {
info.setWalletAddress(walletAddress);
this.info = info;
}
}

public void updateJob()
{
BlockTemplate blockTemplate = call(requestJob, BlockTemplate.class);
if (blockTemplate != null) {
this.blockTemplate = blockTemplate;
}
}

public SubmitBlock submitBlock(Miner miner, String[] x)
{
final BigInteger height = getCurrentJob().getHeight();
JSONObject body = body("submitblock", x);
RequestBodyEntity request = Unirest.post(this.daemonURL).header("Content-Type", "application/json").body(body);

SubmitBlock block = call(request, SubmitBlock.class);

if (block != null) {
block.setMinerIp(miner.getIp());
block.setHeight(height);
block.setTimestamp(System.currentTimeMillis());
stats.addSubmitBlock(block);
miner.addBlocksFound(1);
updateJob();
}

return block;
}

private JSONObject body(String method)
{
return body(method, null);
}

private JSONObject body(String method, Object params)
{
JSONObject json = new JSONObject().put("jsonrpc", "2.0").put("id", 1).put("method", method);
if (params != null) {
json.put("params", params);
}

return json;
}

private <T> T call(RequestBodyEntity body, Class<T> clazz)
{
HttpResponse<String> response;
try {
response = body.asString();
} catch (UnirestException e) {
log.error("An error has occured while retrieving data from daemon!");
e.printStackTrace();
return null;
}

T data = null;
String result = response.getBody();
if (result != null && !result.isEmpty())
{
JsonObject jsonResponse = new JsonParser().parse(result).getAsJsonObject();

if (jsonResponse.has("result"))
{
data = gson.fromJson(jsonResponse.get("result").getAsJsonObject(), clazz);
} else {
log.warn("Error while retrieving data: {}", jsonResponse);
}
}

return data;
}

public BlockTemplate getCurrentJob()
{
return blockTemplate;
}

public Info getInfo()
{
return info;
}
}

+ 21
- 0
src/main/java/fr/slixe/mining/service/SessionManager.java View File

@@ -0,0 +1,21 @@
package fr.slixe.mining.service;

import fr.litarvan.paladin.ISessionManager;
import fr.litarvan.paladin.Session;
import fr.litarvan.paladin.http.Request;
import fr.litarvan.paladin.http.Response;

public class SessionManager implements ISessionManager {

@Override
public Session get(Request request, Response response) {
return null;
}

@Override
public long getExpirationDelay() {
return 0;
}

@Override
public void setExpirationDelay(long expirationDelay) {}}

+ 48
- 0
src/main/java/fr/slixe/mining/service/StatsService.java View File

@@ -0,0 +1,48 @@
package fr.slixe.mining.service;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import com.google.inject.Singleton;

import fr.slixe.mining.structures.Miner;
import fr.slixe.mining.structures.SubmitBlock;

@Singleton
public class StatsService {

private final Set<SubmitBlock> blocks = new LinkedHashSet<>();
private final Map<String, Miner> miners = new HashMap<>();

public void addSubmitBlock(SubmitBlock block)
{
this.blocks.add(block);
}

public Miner createMiner(String ip)
{
Miner miner = new Miner(ip);
miners.put(ip, miner);

return miner;
}

public Optional<Miner> findMiner(String ip)
{
return Optional.ofNullable(miners.get(ip));
}

public Collection<SubmitBlock> getBlocks()
{
return blocks;
}

public Collection<Miner> getMiners()
{
return miners.values();
}
}

+ 79
- 0
src/main/java/fr/slixe/mining/structures/BlockTemplate.java View File

@@ -0,0 +1,79 @@
package fr.slixe.mining.structures;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;

public class BlockTemplate {

@SerializedName("blocktemplate_blob") //used by Gson
@JsonProperty("blocktemplate_blob") //used by Jackson
private String templateBlob;

@SerializedName("blockhashing_blob")
@JsonProperty("blockhashing_blob")
private String hashingBlob;

@SerializedName("prev_hash")
@JsonProperty("prev_hash")
private String prevHash;

@SerializedName("expected_reward")
@JsonProperty("expected_reward")
private BigInteger expectedReward;

@SerializedName("reserved_offset")
@JsonProperty("reserved_offset")
private BigInteger reservedOffset;

private BigInteger difficulty;
private BigInteger height;
private BigInteger epoch;
private String status;

public String getTemplateBlob()
{
return templateBlob;
}

public String getHashingBlob()
{
return hashingBlob;
}

public String getPrevHash()
{
return prevHash;
}

public BigInteger getExpectedReward()
{
return expectedReward;
}

public BigInteger getReservedOffset()
{
return reservedOffset;
}

public BigInteger getDifficulty()
{
return difficulty;
}

public BigInteger getHeight()
{
return height;
}

public BigInteger getEpoch()
{
return epoch;
}

public String getStatus()
{
return status;
}
}

+ 16
- 0
src/main/java/fr/slixe/mining/structures/FakeRPCResult.java View File

@@ -0,0 +1,16 @@
package fr.slixe.mining.structures;

public class FakeRPCResult<T> {

private T result;

public FakeRPCResult(T data)
{
this.result = data;
}

public T getResult()
{
return result;
}
}

+ 140
- 0
src/main/java/fr/slixe/mining/structures/Info.java View File

@@ -0,0 +1,140 @@
package fr.slixe.mining.structures;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;

public class Info {

@SerializedName("alt_blocks_count")
@JsonProperty("alt_blocks_count")
private BigInteger altBlocksCount;
private BigInteger difficulty;
private BigInteger height;
@SerializedName("stableheight")
@JsonProperty("stableheight")
private BigInteger stableHeight;
@SerializedName("topoheight")
@JsonProperty("topoheight")
private BigInteger topoHeight;
@SerializedName("averageblocktime50")
@JsonProperty("averageblocktime50")
private double averageBlockTime50;
private BigInteger target;
@SerializedName("target_height")
@JsonProperty("target_height")
private BigInteger targetHeight;
private boolean testnet;
@SerializedName("top_block_hash")
@JsonProperty("top_block_hash")
private String topBlockHash;
@SerializedName("tx_count")
@JsonProperty("tx_count")
private BigInteger txCount;
@SerializedName("tx_pool_size")
@JsonProperty("tx_pool_size")
private BigInteger txPoolSize;
@SerializedName("dynamic_fee_per_kb")
@JsonProperty("dynamic_fee_per_kb")
private BigInteger dynamicFeePerKb;
@SerializedName("total_supply")
@JsonProperty("total_supply")
private BigInteger totalSupply;
@SerializedName("median_block_size")
@JsonProperty("median_block_size")
private BigInteger medianBlockSize;
private String version;

public BigInteger getAltBlocksCount()
{
return altBlocksCount;
}

public BigInteger getDifficulty()
{
return difficulty;
}

public BigInteger getHeight()
{
return height;
}

public BigInteger getStableHeight()
{
return stableHeight;
}

public BigInteger getTopoHeight()
{
return topoHeight;
}

public double getAverageBlockTime50()
{
return averageBlockTime50;
}

public BigInteger getTarget()
{
return target;
}

public BigInteger getTargetHeight()
{
return targetHeight;
}

public boolean isTestnet()
{
return testnet;
}

public String getTopBlockHash()
{
return topBlockHash;
}

public BigInteger getTxCount()
{
return txCount;
}

public BigInteger getTxPoolSize()
{
return txPoolSize;
}

public BigInteger getDynamicFeePerKb()
{
return dynamicFeePerKb;
}

public BigInteger getTotalSupply()
{
return totalSupply;
}

public BigInteger getMedianBlockSize()
{
return medianBlockSize;
}

public String getVersion()
{
return version;
}

private String walletAddress;

public String getWalletAddress()
{
return walletAddress;
}

public void setWalletAddress(String walletAddress)
{
this.walletAddress = walletAddress;
}
}

+ 81
- 0
src/main/java/fr/slixe/mining/structures/Miner.java View File

@@ -0,0 +1,81 @@
package fr.slixe.mining.structures;

public class Miner {

private String ip;
private String workerName;
private long lastJobRequest;
private long lastBlockSent;
private int blocksFound;
private long lastUpdate;

public Miner() {}

public Miner(String ip)
{
this.ip = ip;
this.lastJobRequest = System.currentTimeMillis();
this.lastBlockSent = -1;
this.blocksFound = 0;
}

public String getIp()
{
return ip;
}

public String getWorkerName()
{
return workerName;
}

public void setWorkerName(String workerName)
{
this.workerName = workerName;
}

public long getLastJobRequest()
{
return lastJobRequest;
}

public long getLastBlockSent()
{
return lastBlockSent;
}

public int getBlocksFound()
{
return blocksFound;
}

public void setLastJobRequest(long lastJobRequest)
{
this.lastJobRequest = lastJobRequest;
}

public void setLastBlockSent(long lastBlockSent)
{
this.lastBlockSent = lastBlockSent;
}

public void setBlockFounds(int blocksFound)
{
this.blocksFound = blocksFound;
}

public void addBlocksFound(int blocks)
{
this.blocksFound += blocks;
}

public void markAlive()
{
this.lastUpdate = System.currentTimeMillis();
}

public boolean isAlive()
{
return this.lastUpdate + 30 * 1000 > System.currentTimeMillis();
}
}

+ 52
- 0
src/main/java/fr/slixe/mining/structures/SubmitBlock.java View File

@@ -0,0 +1,52 @@
package fr.slixe.mining.structures;

import java.math.BigInteger;

public class SubmitBlock {

private String blid;
private String status;
private String minerIp;
private BigInteger height;
private long timestamp;

public String getBlid()
{
return blid;
}

public String getStatus()
{
return status;
}

public String getMinerIp()
{
return minerIp;
}

public void setMinerIp(String ip)
{
this.minerIp = ip;
}

public BigInteger getHeight()
{
return height;
}

public void setHeight(BigInteger height)
{
this.height = height;
}

public long getTimestamp()
{
return timestamp;
}

public void setTimestamp(long timestamp)
{
this.timestamp = timestamp;
}
}

+ 10
- 0
src/main/resources/config.default.json View File

@@ -0,0 +1,10 @@
{
"port": 8080,
"enableSSL": false,
"keystoreFile": "",
"keystorePassword": "",
"daemonURL": "https://wallet.dero.io:443",
"walletAddress": "dERokevAZEZVJ2N7o39VH81BXBqX9ojtncnPTDMyiVbmYiTXQY93AUCLcor9xsWCKWhYy25ja89ikZWXWab9kXRB7LYfUmbQyS",
"reserveSize": 10,
"job-interval": 200
}

+ 29
- 0
src/main/resources/config/app.config.groovy View File

@@ -0,0 +1,29 @@
package config;
import java.util.concurrent.TimeUnit
import fr.litarvan.paladin.http.AcceptCrossOriginRequestsMiddleware
import fr.slixe.mining.http.controller.MainController
[
sessionDuration: TimeUnit.DAYS.toMillis(2), // 2 days
/**
* The app controllers, call them whatever you want to
*/
controllers: [
main: MainController
],
routeMiddlewares:
[
cors: AcceptCrossOriginRequestsMiddleware
],
/**
* Global middlewares (applied on all routes)
*/
globalMiddlewares: [
AcceptCrossOriginRequestsMiddleware
]
]

+ 12
- 0
src/main/resources/config/routes.groovy View File

@@ -0,0 +1,12 @@
package config;
group '/api', {
get '/job'
get '/info'
get '/miners'
get '/blocks'
}, [
action: "main"
]
post '/json_rpc', 'main:jsonRpc'

Loading…
Cancel
Save