Commit bc3e9084 authored by Sharon Urban's avatar Sharon Urban

source

parent f582b5b6
namespace OracleExample.AssemblyInfo
open System.Reflection
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[<assembly: AssemblyTitle("OracleExample")>]
[<assembly: AssemblyDescription("")>]
[<assembly: AssemblyConfiguration("")>]
[<assembly: AssemblyCompany("")>]
[<assembly: AssemblyProduct("OracleExample")>]
[<assembly: AssemblyCopyright("Copyright © $year$")>]
[<assembly: AssemblyTrademark("")>]
[<assembly: AssemblyCulture("")>]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[<assembly: ComVisible(false)>]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[<assembly: Guid("AA5BBFDA-2619-4D03-B32C-0907B037FF42")>]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [<assembly: AssemblyVersion("1.0.*")>]
[<assembly: AssemblyVersion("1.0.0.0")>]
[<assembly: AssemblyFileVersion("1.0.0.0")>]
do
()
\ No newline at end of file
module DataAccess
open MongoDB.Bson
open MongoDB.Driver
open MongoDB.FSharp
open System
[<Literal>]
let ConnectionString = "mongodb://localhost"
[<Literal>]
let DbName = "oracle"
[<Literal>]
let CollectionName = "data"
type Data = {
id : BsonObjectId
date: DateTime
data : (string * decimal) array
}
let client = MongoClient(ConnectionString)
let db = client.GetDatabase(DbName)
let collection = db.GetCollection<Data>(CollectionName)
let insert data =
{
id = MongoDB.Bson.BsonObjectId(MongoDB.Bson.ObjectId.GenerateNewId())
date = DateTime.Today
data = data
}
|> collection.InsertOne
data
let find date =
let records = collection.Find(fun x -> x.date = date).ToEnumerable()
if Seq.length records = 1 then
(Seq.head records).data
|> Some
else
None
\ No newline at end of file
module Json
open FSharp.Data
type RawResultJson = JsonProvider<"""
{
"data": [
{
"identifier": "AAPL",
"value": 208.22
}
]
}""">
type AuditPathResponseJson = JsonProvider<"""
{
"auditPath": [
"abcd123"
]
}""">
module Merkle
open Infrastructure
open Consensus
open Hash
open Util
let hashLeaf (identifier, value:decimal) =
[
getBytes identifier
[| (byte ';') |]
BigEndianBitConverter.uint32ToBytes (1000u * uint32 value)
]
|> Array.concat
|> Hash.compute
module Main
open System
open FSharp.Configuration
open FSharp.Data
open Infrastructure
open Result
open Consensus
open Serialization
open FsBech32
open Json
open Http
open Util
open Server
open Api.Types
type Config = YamlConfig<"config.yaml">
[<EntryPoint>]
let main _ =
let intrinioUserName = getEnv "intrinio_user_name"
let intrinioPassword = getEnv "intrinio_password"
let zenPassword = getEnv "zen_account_password"
let basicAuthHeader =
sprintf "%s:%s" intrinioUserName intrinioPassword
|> getBytes
|> Convert.ToBase64String
let config = new Config()
config.Load("config.yaml")
initHttpServer config.api
while true do
printfn "fetching..."
Exception.resultWrap<String> (fun _ ->
Http.RequestString (
"https://api.intrinio.com/data_point",
httpMethod = "GET",
query = [
"identifier", String.Join(",", config.tickers)
"item", "close_price"
],
headers = [
"Authorization", basicAuthHeader
]
)) "error getting provider data"
>>= (fun raw ->
printfn "parsing..."
Exception.resultWrap<RawResultJson.Root> (
fun _ -> RawResultJson.Parse(raw)) "error parsing provider data")
<@> fun x -> x.Data
<@> Array.map (fun x -> x.Identifier, x.Value)
<@> DataAccess.insert
<@> Array.map Merkle.hashLeaf
<@> Array.toList
<@> MerkleTree.computeRoot
<@> (Hash.bytes >> Zen.Types.Data.data.Hash >> Data.serialize >> Base16.encode)
<@> (fun messageBody ->
(new ContractExecuteRequestJson.Root(
config.contract,
"Add",
messageBody,
new ContractExecuteRequestJson.Options(false, "m/44'/258'/0'/3/0"),
Array.empty,
zenPassword
)))
>>= (fun json ->
printfn "making a commitment on the blockchain..."
Exception.resultWrap<HttpResponse> (fun _ ->
(sprintf "http://%s/wallet/contract/execute" config.nodeApi)
|> json.JsonValue.Request) "error communicating with zen-node"
)
>>= (fun response ->
if response.StatusCode <> 200 then
sprintf "could not execute contract: %A" response.Body
|> Error
else
Ok ())
|> Result.mapError (printfn "%A")
|> ignore
printfn "waiting..."
Threading.Thread.Sleep (1000 * 60 * 60 * config.interval)
0
\ No newline at end of file
module Server
open FsNetMQ
open FSharp.Data.HttpRequestHeaders
open FSharp.Control.Reactive
open Infrastructure.Http
open Consensus
open FSharp.Data
module Actor = FsNetMQ.Actor
let initHttpServer address =
Actor.create (fun shim ->
use poller = Poller.create ()
use observer = Poller.registerEndMessage poller shim
use httpAgent = Server.create poller address
use observer =
Server.observable httpAgent |>
Observable.subscribeWithError (fun (request,reply) ->
match request with
| Get ("/auditpath", query) ->
match Map.tryFind "date" query, Map.tryFind "ticker" query with
| Some date, Some ticker ->
let data =
date
|> Util.parseDate
|> Option.bind DataAccess.find
|> Option.map Array.toList
let index =
data
|> Option.map (List.map fst)
|> Option.map (List.findIndex ((=) ticker))
let hashes =
data
|> Option.map (List.map Merkle.hashLeaf)
match Option.map2 MerkleTree.createAuditPath hashes index with
| Some data ->
data
|> List.toArray
|> Array.map Hash.toString
|> Array.map JsonValue.String
|> JsonValue.Array
|> JsonContent
|> reply StatusCode.OK
| None ->
reply StatusCode.BadRequest (TextContent "ticker not found or bad date format")
| _ ->
reply StatusCode.BadRequest (TextContent "missing timestamp and ticker")
| _ ->
reply StatusCode.NotFound NoContent
) (fun error ->
printfn "error %A" error
raise error
)
Actor.signal shim
Poller.run poller
)
|> ignore
module Util
open System
open System.Text
let getBytes value =
ASCIIEncoding.ASCII.GetBytes (value:string)
let getEnv key =
let value = Environment.GetEnvironmentVariable(key)
if String.IsNullOrEmpty value then
failwith <| sprintf "missing environment variable: %s" key
else
value
let parseDate date =
try
DateTime.ParseExact(date, "yyyy-MM-dd", Globalization.CultureInfo.InvariantCulture)
|> Some
with _ ->
None
\ No newline at end of file
nodeApi: 127.0.0.1:11567
api: 127.0.0.1:8085
tickers:
- AAPL
- GOOG
- TRIP
- INTC
- ZNGA
- MSFT
- FB
- AAL
- ADSK
- AMAT
- AMZN
- BIDU
- CHKP
- CMCSA
- FOX
- PYPL
- QCOM
- AABA
- TSLA
interval: 1
contract: ctzn1qqqqqqqrjguhzhxwr2jzn3neqd07707mses6s7zj3ymxntad8xx09r8qkuyeemac9
\ No newline at end of file
FSharp.Configuration
FSharp.Core
Fsharp.Data
FsNetMQ
FSharp.Control.Reactive
System.Reactive.Compatibility
Zulib
Infrastructure
Consensus
Api
MongoDB.Driver
MongoDB.FSharp
\ No newline at end of file
This diff is collapsed.

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "zen-oracle", "zen-oracle.fsproj", "{AA5BBFDA-2619-4D03-B32C-0907B037FF42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA5BBFDA-2619-4D03-B32C-0907B037FF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA5BBFDA-2619-4D03-B32C-0907B037FF42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA5BBFDA-2619-4D03-B32C-0907B037FF42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA5BBFDA-2619-4D03-B32C-0907B037FF42}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment