Skip to content

Commit

Permalink
Merge pull request #11 from DaoCasino/develop
Browse files Browse the repository at this point in the history
v1.0.0
  • Loading branch information
justefg authored Apr 27, 2020
2 parents 1ee62a1 + f3efd5a commit f5de94c
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 79 deletions.
57 changes: 57 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# use the latest ubuntu environment (18.04) available on travis
dist: bionic

language: go
go:
- 1.13.4

services:
- docker

env:
- IMG_NAME=daocasino/casinoback DOCKER_TAG_LATEST=true DOCKER_REGISTRY=registry.hub.docker.com

before_install:
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin

cache:
directories:
- $HOME/.ccache
timeout: 1000

# Only clone the most recent commit.
git:
depth: 1


# Don't email me the results of the test runs.
notifications:
email: false

# Anything in before_script that returns a nonzero exit code will flunk the
# build and immediately stop. It's sorta like having set -e enabled in bash.
# We can download and extract the golangci-lint binary in one (long) command.
before_script:
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $GOPATH/bin v1.23.1

# script always runs to completion (set +e). If we have linter issues AND a
# failing test, we want to see both. Configure golangci-lint with a
# .golangci.yml file at the top level of your repo.
jobs:
include:
- stage: lint & test
script:
- golangci-lint run
- go test -v -race -coverprofile=coverage.txt -covermode=atomic .
- stage: build & push
script:
- docker build -t $IMG_NAME:$TRAVIS_BRANCH -f Dockerfile .
- |
docker push $IMG_NAME:$TRAVIS_BRANCH
if [ $TRAVIS_BRANCH == "master" ]; then
docker tag $IMG_NAME:master $IMG_NAME:latest
docker push $IMG_NAME:latest
fi
after_success:
- bash <(curl -s https://codecov.io/bash)
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:latest AS builder
ENV GO111MODULE=on
RUN mkdir -p /build
ADD . /build
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o casino .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /build/casino /usr/bin
CMD ["casino"]
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# Casino Backend

## Build Status

Branch|Build Status
---|---
Master|[![master](https://travis-ci.org/DaoCasino/casino-backend?branch=master)](https://travis-ci.org/DaoCasino/casino-backend)
Develop|[![develop](https://travis-ci.org/DaoCasino/casino-backend?branch=develop)](https://travis-ci.org/DaoCasino/casino-backend)

209 changes: 199 additions & 10 deletions app.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
package main

import (
"context"
"crypto/rsa"
"encoding/hex"
"encoding/json"
"github.com/eoscanada/eos-go/ecc"
broker "github.com/DaoCasino/platform-action-monitor-client"
"github.com/eoscanada/eos-go"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"github.com/zenazn/goji/graceful"
"os"
"os/signal"
"strings"
"sync"
"syscall"

"io/ioutil"
"net/http"
)

Expand All @@ -14,21 +26,165 @@ type JsonResponse = map[string]interface{}

type App struct {
Router *mux.Router
PrivateKey *ecc.PrivateKey
TopicOffsetPath string
Broker struct {
TopicOffsetPath string
Url string
TopicID broker.EventType
}
BlockChain struct {
API *eos.API
KeyBag eos.KeyBag
ChainID string
CasinoAccountName string
SignidiceKey *rsa.PrivateKey
}
}

func (app *App) Initialize(pk *ecc.PrivateKey, offsetPath string, level string) {
func (app *App) Initialize(wif string, blockChainUrl string, chainID string,
offsetPath string, brokerURL string, topicID broker.EventType,
casinoAccountName, level string, signidiceKey *rsa.PrivateKey,
) {
InitLogger(level)
if strings.ToLower(level) == "debug" {
broker.EnableDebugLogging()
}
log.Debug().Msg("initializing app")

app.Router = mux.NewRouter()
app.PrivateKey = pk
app.TopicOffsetPath = offsetPath
app.BlockChain.API = eos.New(blockChainUrl)
app.BlockChain.ChainID = chainID
app.BlockChain.CasinoAccountName = casinoAccountName
app.BlockChain.SignidiceKey = signidiceKey

log.Debug().Msg("Reading private key from wif")
if app.BlockChain.KeyBag.Add(wif) != nil {
log.Panic().Msg("Malformed private key")
}
app.BlockChain.API.SetSigner(&app.BlockChain.KeyBag)
app.Broker.Url = brokerURL
app.Broker.TopicOffsetPath = offsetPath
app.Broker.TopicID = topicID

InitLogger(level)
app.InitializeRoutes()
}

type BrokerData struct {
Digest eos.Checksum256 `json:"digest"`
}

func (app *App) processEvent(event *broker.Event) {
log.Info().Msgf("Processing event %+v", event)
var data BrokerData
parseError := json.Unmarshal(event.Data, &data)

if parseError != nil {
log.Warn().Msg("Couldnt get digest from event")
return
}

api := app.BlockChain.API
signature, signError := rsaSign(data.Digest, app.BlockChain.SignidiceKey)

if signError != nil {
log.Warn().Msg("Couldnt sign signidice_part_2, reason=" + signError.Error())
return
}

trx, packedTx, err := GetSigndiceTransaction(api, event.Sender, app.BlockChain.CasinoAccountName, event.RequestID, signature)

if err != nil {
log.Warn().Msg("couldn't form transaction, reason: " + err.Error())
return
}

log.Debug().Msgf("%+v", trx)

result, sendError := api.PushTransaction(packedTx)
if sendError != nil {
log.Warn().Msg("Failed to send transaction, reason: " + sendError.Error())
return
}
log.Debug().Msg("Successfully signed and sent txn, id: " + result.TransactionID)
}

func (app *App) RunEventListener(parentContext context.Context, wg *sync.WaitGroup) {

go func(parentContext context.Context) {
defer wg.Done()
events := make(chan *broker.EventMessage)

listener := broker.NewEventListener(app.Broker.Url, events)
ctx, cancel := context.WithCancel(context.Background())

if err := listener.ListenAndServe(ctx); err != nil {
log.Panic().Msg(err.Error())
}

defer cancel()

offsetPath := app.Broker.TopicOffsetPath
offset := readOffset(offsetPath)
topicID := app.Broker.TopicID

log.Debug().Msgf("Subscribing to event type %+v with an offset of %+v", topicID, offset)
_, err := listener.Subscribe(topicID, offset)

if err != nil {
log.Error().Msg("Failed to subscribe")
return
}

for {
select {
case <-parentContext.Done():
log.Debug().Msg("Terminating event listener")
_, err := listener.Unsubscribe(topicID)
if err != nil {
log.Warn().Msg("Failed to unsubscribe")
} else {
log.Debug().Msg("Event listener successfully terminated")
}
return
case eventMessage, ok := <-events:
if !ok {
log.Info().Msg("Failed to read events")
break
}
if len(eventMessage.Events) == 0 {
log.Debug().Msg("Gotta event message with no events")
break
}
log.Debug().Msgf("Processing %+v events", len(eventMessage.Events))
for _, event := range eventMessage.Events {
go app.processEvent(event)
}
offset = eventMessage.Events[len(eventMessage.Events) - 1].Offset + 1
writeOffset(offsetPath, offset)
}
}
}(parentContext)
}

func (app *App) Run(addr string) {
log.Error().Msg(http.ListenAndServe(addr, app.Router).Error())
parentContext, cancel := context.WithCancel(context.Background())
log.Debug().Msg("starting http server")
go func() {
log.Error().Msg(graceful.ListenAndServe(addr, app.Router).Error())
}()
log.Debug().Msg("stating event listener")
var wg sync.WaitGroup
wg.Add(1)
app.RunEventListener(parentContext, &wg)

// Handle signals
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
log.Debug().Msg("Waiting for signal")
<-done
log.Info().Msg("Terminating service")
cancel()
wg.Wait()
log.Info().Msg("Service successfully terminated")
}

func respondWithError(writer ResponseWriter, code int, message string) {
Expand All @@ -39,7 +195,10 @@ func respondWithJSON(writer ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(code)
writer.Write(response)
_, err := writer.Write(response)
if err != nil {
log.Warn().Msg("Failed to respond to client")
}
}

func (app *App) PingQuery(writer ResponseWriter, req *Request) {
Expand All @@ -49,7 +208,37 @@ func (app *App) PingQuery(writer ResponseWriter, req *Request) {

func (app *App) SignQuery(writer ResponseWriter, req *Request) {
log.Info().Msg("Called /sign_transaction")
// TODO
rawTransaction, _ := ioutil.ReadAll(req.Body)
tx := eos.SignedTransaction{}
err := json.Unmarshal(rawTransaction, &tx)
if err != nil {
log.Debug().Msg(err.Error())
respondWithError(writer, http.StatusBadRequest, "failed to deserialize transaction")
return
}

signedTx, signError := app.SignTransaction(&tx)
if signError != nil {
log.Warn().Msg(signError.Error())
respondWithError(writer, http.StatusInternalServerError, "failed to sign transaction")
return
}
packedTrx, _ := signedTx.Pack(eos.CompressionNone)
result, sendError := app.BlockChain.API.PushTransaction(packedTrx)
if sendError != nil {
log.Debug().Msg(sendError.Error())
respondWithError(writer, http.StatusBadRequest, "failed to send transaction to the blockchain: " + sendError.Error())
return
}

respondWithJSON(writer, http.StatusOK, JsonResponse{"txid": result.TransactionID})
}

func(app *App) SignTransaction(trx *eos.SignedTransaction) (*eos.SignedTransaction, error) {
blockchain := app.BlockChain
publicKeys, _ := blockchain.KeyBag.AvailableKeys()
chainID, _ := hex.DecodeString(blockchain.ChainID)
return blockchain.KeyBag.Sign(trx, chainID, publicKeys[0])
}

func (app *App) InitializeRoutes() {
Expand Down
42 changes: 42 additions & 0 deletions blockchain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"github.com/eoscanada/eos-go"
)

func NewSigndice(contract, casinoAccount string, requestID uint64, signature string) *eos.Action {
return &eos.Action{
Account: eos.AN(contract),
Name: eos.ActN("sgdicesecond"),
Authorization: []eos.PermissionLevel{
{Actor: eos.AN(casinoAccount), Permission: eos.PN("signidice")},
},
ActionData: eos.NewActionData(Signidice{
requestID,
signature,
}),
}
}

// Game contract's sgdicesecond action parameters
type Signidice struct {
RequestID uint64 `json:"req_id"`
Signature string `json:"sign"`
}


func GetSigndiceTransaction(api *eos.API, contract, casinoAccount string,requestID uint64, signature string) (*eos.SignedTransaction, *eos.PackedTransaction, error) {
action := NewSigndice(contract, casinoAccount, requestID, signature)
txOpts := &eos.TxOptions{}

if err := txOpts.FillFromChain(api); err != nil {
return nil, nil, err
}
tx := eos.NewTransaction([]*eos.Action{action}, txOpts)
signedTx, packedTx, err := api.SignTransaction(tx, txOpts.ChainID, eos.CompressionNone)
if err != nil {
return nil, nil, err
}
return signedTx, packedTx, nil
}

16 changes: 9 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module github.com/DaoCasino/casino-backend

go 1.12
go 1.13

require (
github.com/BurntSushi/toml v0.3.1
github.com/eoscanada/eos-go v0.9.0
github.com/gorilla/mux v1.7.4
github.com/kelseyhightower/envconfig v1.4.0
github.com/rs/zerolog v1.18.0
github.com/stretchr/testify v1.4.0
github.com/BurntSushi/toml v0.3.1
github.com/DaoCasino/platform-action-monitor-client v0.0.0-20200413133148-52406924efbc
github.com/eoscanada/eos-go v0.9.0
github.com/gorilla/mux v1.7.4
github.com/kelseyhightower/envconfig v1.4.0
github.com/rs/zerolog v1.18.0
github.com/stretchr/testify v1.5.1
github.com/zenazn/goji v0.9.0
)
Loading

0 comments on commit f5de94c

Please sign in to comment.