Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:ergoplatform/oracle-core into de…
Browse files Browse the repository at this point in the history
…v-reward
  • Loading branch information
kushti committed Mar 11, 2024
2 parents 8dd2f48 + 3a22293 commit 9f0fdf0
Show file tree
Hide file tree
Showing 18 changed files with 346 additions and 73 deletions.
88 changes: 61 additions & 27 deletions .github/workflows/docker_crossbuild_publish.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name: Cross-Compile Docker Build and Push

on:
pull_request:
types:
- opened
- synchronize
- closed
release:
types: [published]
workflow_dispatch:

env:
REGISTRY_IMAGE: ergoplatform/oracle-core

jobs:
build:
Expand All @@ -23,45 +24,78 @@ jobs:
uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2

- name: Generate Docker metadata
id: metadata
uses: docker/metadata-action@v3
uses: docker/metadata-action@v4
with:
images: greenden/oracle-core
tags: |
type=ref,event=tag
- name: Build images
uses: docker/build-push-action@v3
images: ${{ env.REGISTRY_IMAGE }}

- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v4
with:
context: .
platforms: ${{ matrix.platform }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
TARGETPLATFORM=${{ matrix.platform }}
CCARCH=${{ matrix.ccarch }}
push: false
load: true

push:
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v3
with:
name: digests
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

merge:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download digests
uses: actions/download-artifact@v3
with:
name: digests
path: /tmp/digests

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

uses: docker/setup-buildx-action@v2

- name: Generate Docker metadata
id: metadata
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY_IMAGE }}
flavor: |
latest=true
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Combine and Push to DockerHub
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64
tags: greenden/oracle-core:${{ github.ref_name }}, greenden/oracle-core:latest
push: true

- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.metadata.outputs.version }}
16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
# Oracle Core v2.0

The oracle core requires that the user has access to a full node wallet in order to create txs & perform UTXO-set scanning. Furthermore each oracle core is designed to work with only a single oracle pool. If an operator runs several oracles in several oracle pools then a single full node can be used, but several instances of oracle cores must be run (and set with different api ports).
The oracle core requires that the user has access to a full node wallet in order to create txs & perform UTXO-set scanning. Furthermore, each oracle core is designed to work with only a single oracle pool. If an operator runs several oracles in several oracle pools then a single full node can be used, but several instances of oracle cores must be run (and set with different API ports).

The current oracle core is built to run the protocol specified in the [EIP-0023 PR](https://github.com/ergoplatform/eips/pull/41).

## Roadmap

### In progress

- v2.0-beta. Run a public oracle pool on testnet. See [planned tasks](https://github.com/ergoplatform/oracle-core/milestone/5)

### Next

- v2.0-RC. Launch on the mainnet. See [planned tasks](https://github.com/ergoplatform/oracle-core/milestone/4)

## Getting started

### Docker Image

AMD64 and ARM64 images are available from [Docker Hub Repo](https://hub.docker.com/r/ergoplatform/oracle-core)

The container runs under oracle-core user ( 9010 uid ), if using bind mount for container's /data folder ( where config files and other data lives ), set the container's uid for the host's folder ownership ( ex: chown -R 9010:9010 oracle_data ).
The container runs under oracle-core user (9010 uid), if using bind mount for container's /data folder (where config files and other data lives), set the container's uid for the host's folder ownership ( ex: chown -R 9010:9010 oracle_data ).

An example docker run command:

Expand Down Expand Up @@ -62,7 +52,7 @@ oracle-core generate-oracle-config
and set the required parameters:

- `oracle_address` - a node's address that will be used by this oracle-core instance(pay tx fees, keep tokens, etc.). Make sure it has coins;
- `node_url` node URL;
- `node_url` node URL;

Set the environment variable `ORACLE_NODE_API_KEY` to the node's API key. You can put it in the `.secrets` file and then run `source .secrets` to load it into the environment. This way, the key does not get stored in the shell history.

Expand Down
3 changes: 2 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[package]
name = "oracle-core"
version = "2.0.0"
version = "2.0.1"
authors = [
"Robert Kornacki <[email protected]>",
"@greenhat",
"@kettlebell",
"@SethDusek",
"@kushti",
]
edition = "2021"

Expand Down
24 changes: 20 additions & 4 deletions core/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ async fn pool_status(oracle_pool: Arc<OraclePool>) -> Result<Json<serde_json::Va
}

fn pool_status_sync(oracle_pool: Arc<OraclePool>) -> Result<Json<serde_json::Value>, ApiError> {
let node_api = NodeApi::new(ORACLE_SECRETS.node_api_key.clone(), &ORACLE_CONFIG.node_url);
let node_api = NodeApi::new(
ORACLE_SECRETS.node_api_key.clone(),
ORACLE_SECRETS.wallet_password.clone(),
&ORACLE_CONFIG.node_url,
);
let current_height = node_api.node.current_block_height()? as u32;
let pool_box = oracle_pool.get_pool_box_source().get_pool_box()?;
let epoch_length = POOL_CONFIG
Expand Down Expand Up @@ -156,7 +160,11 @@ fn pool_status_sync(oracle_pool: Arc<OraclePool>) -> Result<Json<serde_json::Val
/// Block height of the Ergo blockchain
async fn block_height() -> Result<impl IntoResponse, ApiError> {
let current_height = task::spawn_blocking(move || {
let node_api = NodeApi::new(ORACLE_SECRETS.node_api_key.clone(), &ORACLE_CONFIG.node_url);
let node_api = NodeApi::new(
ORACLE_SECRETS.node_api_key.clone(),
ORACLE_SECRETS.wallet_password.clone(),
&ORACLE_CONFIG.node_url,
);
node_api.node.current_block_height()
})
.await
Expand Down Expand Up @@ -197,7 +205,11 @@ async fn oracle_health(oracle_pool: Arc<OraclePool>) -> impl IntoResponse {
}

fn oracle_health_sync(oracle_pool: Arc<OraclePool>) -> Result<OracleHealth, ApiError> {
let node_api = NodeApi::new(ORACLE_SECRETS.node_api_key.clone(), &ORACLE_CONFIG.node_url);
let node_api = NodeApi::new(
ORACLE_SECRETS.node_api_key.clone(),
ORACLE_SECRETS.wallet_password.clone(),
&ORACLE_CONFIG.node_url,
);
let current_height = (node_api.node.current_block_height()? as u32).into();
let epoch_length = POOL_CONFIG
.refresh_box_wrapper_inputs
Expand Down Expand Up @@ -239,7 +251,11 @@ async fn pool_health(oracle_pool: Arc<OraclePool>) -> impl IntoResponse {
}

fn pool_health_sync(oracle_pool: Arc<OraclePool>) -> Result<PoolHealth, ApiError> {
let node_api = NodeApi::new(ORACLE_SECRETS.node_api_key.clone(), &ORACLE_CONFIG.node_url);
let node_api = NodeApi::new(
ORACLE_SECRETS.node_api_key.clone(),
ORACLE_SECRETS.wallet_password.clone(),
&ORACLE_CONFIG.node_url,
);
let current_height = (node_api.node.current_block_height()? as u32).into();
let pool_box = &oracle_pool.get_pool_box_source().get_pool_box()?;
let pool_box_height = pool_box.get_box().creation_height.into();
Expand Down
15 changes: 15 additions & 0 deletions core/src/box_kind/oracle_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@ impl CollectedOracleBox {
&self.ergo_box
}

pub fn reward_token(&self) -> SpecToken<RewardTokenId> {
let token = self
.get_box()
.tokens
.as_ref()
.unwrap()
.get(1)
.unwrap()
.clone();
SpecToken {
token_id: RewardTokenId::from_token_id_unchecked(token.token_id),
amount: token.amount,
}
}

pub fn public_key(&self) -> EcPoint {
self.ergo_box
.get_register(NonMandatoryRegisterId::R4.into())
Expand Down
11 changes: 7 additions & 4 deletions core/src/cli_commands/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ use crate::{
},
explorer_api::wait_for_txs_confirmation,
node_interface::{
assert_wallet_unlocked,
node_api::{NodeApi, NodeApiError},
SignTransactionWithInputs, SubmitTransaction,
try_ensure_wallet_unlocked, SignTransactionWithInputs, SubmitTransaction,
},
oracle_config::{BASE_FEE, ORACLE_CONFIG, ORACLE_SECRETS},
oracle_types::{BlockHeight, EpochCounter},
Expand All @@ -69,8 +68,12 @@ pub fn bootstrap(config_file_name: String) -> Result<(), anyhow::Error> {
let s = std::fs::read_to_string(config_file_name)?;
let config: BootstrapConfig = serde_yaml::from_str(&s)?;

let node_api = NodeApi::new(ORACLE_SECRETS.node_api_key.clone(), &oracle_config.node_url);
assert_wallet_unlocked(&node_api.node);
let node_api = NodeApi::new(
ORACLE_SECRETS.node_api_key.clone(),
ORACLE_SECRETS.wallet_password.clone(),
&oracle_config.node_url,
);
try_ensure_wallet_unlocked(&node_api);
let change_address = node_api.get_change_address()?;
debug!("Change address: {:?}", change_address);
let erg_value_per_box = config.oracle_contract_parameters.min_storage_rent;
Expand Down
3 changes: 2 additions & 1 deletion core/src/cli_commands/prepare_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ impl<'a> PrepareUpdate<'a> {
token_box_guard,
self.input.height.0,
);
builder.mint_token(token.clone(), token_name, token_desc, 1);
builder.mint_token(token.clone(), token_name, token_desc, 0);
let mut output_candidates = vec![builder.build()?];

let remaining_funds = ErgoBoxCandidateBuilder::new(
Expand Down Expand Up @@ -628,6 +628,7 @@ rescan_height: 141887
node_url: http://10.94.77.47:9052
node_api_key: hello
base_fee: 1100000
scan_start_height: 0
log_level: ~
core_api_port: 9010
oracle_address: 3Wy3BaCjGDWE3bjjZkNo3aWaMz3cYrePMFhchcKovY9uG9vhpAuW
Expand Down
27 changes: 27 additions & 0 deletions core/src/datapoint_source/bitpanda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::DataPointSourceError;
#[derive(Debug, Clone)]
pub struct BitPanda {}

#[cfg(not(test))]
pub async fn get_kgau_usd() -> Result<AssetsExchangeRate<KgAu, Usd>, DataPointSourceError> {
let url = "https://api.bitpanda.com/v1/ticker";
let resp = reqwest::get(url).await?;
Expand Down Expand Up @@ -34,6 +35,20 @@ pub async fn get_kgau_usd() -> Result<AssetsExchangeRate<KgAu, Usd>, DataPointSo
}
}

#[cfg(test)]
pub async fn get_kgau_usd() -> Result<AssetsExchangeRate<KgAu, Usd>, DataPointSourceError> {
// USD price of 1 gram of gold
let p_float = 66.10;
let usd_per_kgau = KgAu::from_gram(p_float);
let rate = AssetsExchangeRate {
per1: KgAu {},
get: Usd {},
rate: usd_per_kgau,
};
Ok(rate)
}

#[cfg(not(test))]
// Get USD/BTC. Can be used as a redundant source for ERG/BTC through ERG/USD and USD/BTC
pub(crate) async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPointSourceError> {
let url = "https://api.bitpanda.com/v1/ticker";
Expand Down Expand Up @@ -61,6 +76,18 @@ pub(crate) async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPo
}
}

#[cfg(test)]
pub(crate) async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPointSourceError> {
// USD price of BTC
let usd_per_btc = 43827.02;
let rate = AssetsExchangeRate {
per1: Btc {},
get: Usd {},
rate: usd_per_btc,
};
Ok(rate)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
25 changes: 25 additions & 0 deletions core/src/datapoint_source/coincap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::DataPointSourceError;
#[derive(Debug, Clone)]
pub struct CoinCap;

#[cfg(not(test))]
pub async fn get_usd_nanoerg() -> Result<AssetsExchangeRate<Usd, NanoErg>, DataPointSourceError> {
// see https://coincap.io/assets/ergo
let url = "https://api.coincap.io/v2/assets/ergo";
Expand Down Expand Up @@ -34,6 +35,19 @@ pub async fn get_usd_nanoerg() -> Result<AssetsExchangeRate<Usd, NanoErg>, DataP
}
}

#[cfg(test)]
pub async fn get_usd_nanoerg() -> Result<AssetsExchangeRate<Usd, NanoErg>, DataPointSourceError> {
let p_float = 1.661_923_469_67;
let nanoerg_per_usd = NanoErg::from_erg(1.0 / p_float);
let rate = AssetsExchangeRate {
per1: Usd {},
get: NanoErg {},
rate: nanoerg_per_usd,
};
Ok(rate)
}

#[cfg(not(test))]
// Get USD/BTC. Can be used as a redundant source for ERG/BTC through ERG/USD and USD/BTC
pub async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPointSourceError> {
// see https://coincap.io/assets/ergo
Expand Down Expand Up @@ -61,6 +75,17 @@ pub async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPointSour
}
}

#[cfg(test)]
pub async fn get_btc_usd() -> Result<AssetsExchangeRate<Btc, Usd>, DataPointSourceError> {
let usd_per_btc = 43_712.768_005_075_37;
let rate = AssetsExchangeRate {
per1: Btc {},
get: Usd {},
rate: usd_per_btc,
};
Ok(rate)
}

#[cfg(test)]
mod tests {
use super::super::bitpanda;
Expand Down
Loading

0 comments on commit 9f0fdf0

Please sign in to comment.