Skip to content

Commit

Permalink
Merge pull request #3 from singnet/master
Browse files Browse the repository at this point in the history
Pull from parent
  • Loading branch information
raamb authored Aug 22, 2019
2 parents de94498 + 566a887 commit b3328fe
Show file tree
Hide file tree
Showing 63 changed files with 2,166 additions and 380 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
build:
docker:
- image: circleci/golang:1.9-node
- image: circleci/golang:1.10.4-node
working_directory: /go/src/github.com/singnet/snet-daemon
environment:
TRIGGER_BUILD_BRANCH: master
Expand Down
4 changes: 2 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# snet-daemon

[![CircleCI](https://circleci.com/gh/singnet/snet-daemon.svg?style=svg)](https://circleci.com/gh/singnet/snet-daemon)
[![Coverage Status](https://coveralls.io/repos/github/singnet/snet-daemon/badge.svg)](https://coveralls.io/github/singnet/snet-daemon)

SingularityNET Daemon
The daemon is the adapter with which an otherwise SingularityNET-unaware service implementation can be exposed to the SingularityNET platform. It is designed to be deployed as a sidecar proxy alongside the service on a given host.
Expand Down Expand Up @@ -152,8 +153,8 @@ configuration properties can be set using configuration file.

These properties you should usually change before starting daemon for the first
time.
* **blockchain_network_selected** (required; default: "local")
- Name of the network to be used for Daemon possible values are one of (kovan,ropsten,main,local or rinkeby).
* **blockchain_network_selected** (required)
Name of the network to be used for Daemon possible values are one of (kovan,ropsten,main,local or rinkeby).
Daemon will automatically read the Registry address associated with this network For local network ( you can also specify the registry address manually),see the blockchain_network_config.json

* **daemon_end_point** (required;) -
Expand Down Expand Up @@ -185,16 +186,20 @@ metadata][service-configuration-metadata].
when passthrough is disabled, daemon echoes requests back as responses; `false`
reserved mostly for testing purposes.

* **passthrough_endpoint** (required iff `service_type` != `executable`) -
* **passthrough_endpoint** (required if `service_type` != `executable`) -
endpoint to which requests should be proxied for handling by service.

* **executable_path** (required iff `service_type` == `executable`) -
* **executable_path** (required if `service_type` == `executable`) -
path to executable to expose as a service.

#### Other properties

This options are less frequently needed.

* **authentication_address** (required if `You need to update Daemon configurations remotely`)
Contains the Authentication address that will be used to validate all requests to update Daemon configuration remotely
through a user interface ( Operator UI)

* **auto_ssl_domain** (optional; default: `""`) -
domain name for which the daemon should automatically acquire SSL certs from [Let's Encrypt](https://letsencrypt.org/).

Expand Down
17 changes: 17 additions & 0 deletions authutils/auth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package authutils
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/hex"
"errors"
"fmt"
Expand Down Expand Up @@ -105,3 +106,19 @@ func VerifyAddress(address common.Address, otherAddress common.Address) error {
}
return nil
}



func GetSignature(message []byte, privateKey *ecdsa.PrivateKey) (signature []byte) {
hash := crypto.Keccak256(
blockchain.HashPrefix32Bytes,
crypto.Keccak256(message),
)

signature, err := crypto.Sign(hash, privateKey)
if err != nil {
panic(fmt.Sprintf("Cannot sign test message: %v", err))
}

return signature
}
8 changes: 5 additions & 3 deletions authutils/auth_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
package authutils

import (
"github.com/singnet/snet-daemon/config"
"github.com/stretchr/testify/assert"

"math/big"
"testing"

"github.com/singnet/snet-daemon/config"
"github.com/stretchr/testify/assert"
)

func TestCompareWithLatestBlockNumber(t *testing.T) {
config.Vip().Set(config.EthereumJsonRpcEndpointKey, "https://ropsten.infura.io")
config.Vip().Set(config.BlockChainNetworkSelected, "ropsten")
config.Validate()
currentBlockNum, _ := CurrentBlock()
err := CompareWithLatestBlockNumber(currentBlockNum.Add(currentBlockNum, big.NewInt(13)))
Expand Down
233 changes: 233 additions & 0 deletions blockchain/orginzationMetadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package blockchain

import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/singnet/snet-daemon/config"
"github.com/singnet/snet-daemon/ipfsutils"
log "github.com/sirupsen/logrus"
"math/big"
"strings"
"time"
)

/*
This metadata structure defines the organization to group mappings.
Please note , that groups are logical bucketing of managing payments ( same recipient for each group across the services in an organization)
A given Organization will be associated with multiple groups and every group will be associated to a payment
Sample example of the JSON structure from the block chain is given below .
Please note that all the services that belong to a given group in an organization will have the same recipient address.
*/

/*
{
"org_name": "organization_name",
"org_id": "org_id1",
"groups": [
{
"group_name": "default_group2",
"group_id": "99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=",
"payment": {
"payment_address": "0x671276c61943A35D5F230d076bDFd91B0c47bF09",
"payment_expiration_threshold": 40320,
"payment_channel_storage_type": "etcd",
"payment_channel_storage_client": {
"connection_timeout": "5s",
"request_timeout": "3s",
"endpoints": [
"http://127.0.0.1:2379"
]
}
}
},
{
"group_name": "default_group2",
"group_id": "99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=",
"payment": {
"payment_address": "0x671276c61943A35D5F230d076bDFd91B0c47bF09",
"payment_expiration_threshold": 40320,
"payment_channel_storage_type": "etcd",
"payment_channel_storage_client": {
"connection_timeout": "5s",
"request_timeout": "3s",
"endpoints": [
"http://127.0.0.1:2379"
]
}
}
}
]
}*/

type OrganizationMetaData struct {
OrgName string `json:"org_name"`
OrgID string `json:"org_id"`
Groups []Group `json:"groups"`
//This will used to determine which group the daemon belongs to
daemonGroup *Group
daemonGroupID [32]byte
recipientPaymentAddress common.Address
}

type Payment struct {
PaymentAddress string `json:"payment_address"`
PaymentExpirationThreshold *big.Int `json:"payment_expiration_threshold"`
PaymentChannelStorageType string `json:"payment_channel_storage_type"`
PaymentChannelStorageClient PaymentChannelStorageClient `json:"payment_channel_storage_client"`
}

//Structure to hold the individual group details , an Organization can have multiple groups
type Group struct {
GroupName string `json:"group_name"`
GroupID string `json:"group_id"`
PaymentDetails Payment `json:"payment"`
}

//Structure to hold the storage details of the payment
type PaymentChannelStorageClient struct {
ConnectionTimeout string `json:"connection_timeout" mapstructure:"connection_timeout"`
RequestTimeout string `json:"request_timeout" mapstructure:"request_timeout"`
Endpoints []string `json:"endpoints"`
}

//Construct the Organization metadata from the JSON Passed
func InitOrganizationMetaDataFromJson(jsonData string) (metaData *OrganizationMetaData, err error) {
metaData = new(OrganizationMetaData)
err = json.Unmarshal([]byte(jsonData), &metaData)
if err != nil {
log.WithError(err).WithField("jsondata", jsonData)
return nil, err
}

//Check for mandatory validations

if err = setDerivedAttributes(metaData); err != nil {
log.WithError(err)
return nil, err
}
if err = checkMandatoryFields(metaData); err != nil {
return nil,err
}

return metaData, nil
}

func checkMandatoryFields(metaData *OrganizationMetaData) (err error ){
if metaData.daemonGroup.PaymentDetails.PaymentChannelStorageClient.Endpoints == nil {
err = fmt.Errorf("Mandatory field : ETCD Client Endpoints are mising for the Group %v ",metaData.daemonGroup.GroupName)
}
if &metaData.recipientPaymentAddress == nil {
err = fmt.Errorf("Mandatory field : Recepient Address is missing for the Group %v ",metaData.daemonGroup.GroupName)
}
return
}

func setDerivedAttributes(metaData *OrganizationMetaData) (err error) {
if metaData.daemonGroup, err = getDaemonGroup(*metaData); err != nil {
return err
}
metaData.daemonGroupID, err = ConvertBase64Encoding(metaData.daemonGroup.GroupID)
//use the checksum address ( convert the address in to a checksum address and set it back)
metaData.daemonGroup.PaymentDetails.PaymentAddress = ToChecksumAddress(metaData.daemonGroup.PaymentDetails.PaymentAddress)

metaData.recipientPaymentAddress = common.HexToAddress(metaData.daemonGroup.PaymentDetails.PaymentAddress)

return err
}

//Determine the group this Daemon belongs to
func getDaemonGroup(metaData OrganizationMetaData) (group *Group, err error) {
groupName := config.GetString(config.DaemonGroupName)
for _, group := range metaData.Groups {
if strings.Compare(group.GroupName, groupName) == 0 {
return &group, nil
}
}
err = fmt.Errorf("group name %v in config is invalid, "+
"there was no group found with this name in the metadata", groupName)
log.WithError(err)
return nil, err
}

//Will be used to load the Organization metadata when Daemon starts
//To be part of components
func GetOrganizationMetaData() *OrganizationMetaData {
var metadata *OrganizationMetaData
var err error
if config.GetBool(config.BlockchainEnabledKey) {
ipfsHash := string(getMetaDataURI())
metadata, err = GetOrganizationMetaDataFromIPFS(FormatHash(ipfsHash))
} else {
metadata = &OrganizationMetaData{daemonGroup:&Group{}}
}
if err != nil {
log.WithError(err).
Panic("error on retrieving / parsing organization metadata from block chain")
}
return metadata
}

func GetOrganizationMetaDataFromIPFS(hash string) (*OrganizationMetaData, error) {
jsondata := ipfsutils.GetIpfsFile(hash)
return InitOrganizationMetaDataFromJson(jsondata)
}


func getMetaDataURI() []byte {
//Block chain call here to get the hash of the metadata for the given Organization
reg := getRegistryCaller()
orgId := StringToBytes32(config.GetString(config.OrganizationId))

organizationRegistered, err := reg.GetOrganizationById(nil, orgId)
if err != nil || !organizationRegistered.Found {
log.WithError(err).WithField("OrganizationId", config.GetString(config.OrganizationId)).
Panic("Error Retrieving contract details for the Given Organization")
}
return organizationRegistered.OrgMetadataURI[:]
}

//Get the Group ID the Daemon needs to associate itself to , requests belonging to a different group if will be rejected
func (metaData OrganizationMetaData) GetGroupIdString() string {
return metaData.daemonGroup.GroupID
}

// Return the group id in bytes
func (metaData OrganizationMetaData) GetGroupId() [32]byte {
return metaData.daemonGroupID
}

//Pass the group Name and retrieve the details of the payment address/ recipient address.
func (metaData OrganizationMetaData) GetPaymentAddress() common.Address {
return metaData.recipientPaymentAddress
}

//Payment expiration threshold
func (metaData *OrganizationMetaData) GetPaymentExpirationThreshold() *big.Int {
return metaData.daemonGroup.PaymentDetails.PaymentExpirationThreshold
}

//Get the End points of the Payment Storage used to update the storage state
func (metaData OrganizationMetaData) GetPaymentStorageEndPoints() []string {
return metaData.daemonGroup.PaymentDetails.PaymentChannelStorageClient.Endpoints
}


//Get the connection time out defined
func (metaData OrganizationMetaData) GetConnectionTimeOut() ( connectionTimeOut time.Duration) {
connectionTimeOut, err := time.ParseDuration(metaData.daemonGroup.PaymentDetails.PaymentChannelStorageClient.ConnectionTimeout);
if err != nil {
log.Errorf(err.Error())
}
return connectionTimeOut
}

//Get the Request time out defined
func (metaData OrganizationMetaData) GetRequestTimeOut() time.Duration {
timeOut, err := time.ParseDuration(metaData.daemonGroup.PaymentDetails.PaymentChannelStorageClient.RequestTimeout);
if err != nil {
log.Errorf(err.Error())
}
return timeOut
}
41 changes: 41 additions & 0 deletions blockchain/orginzationMetadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package blockchain

import (
"github.com/singnet/snet-daemon/config"
"github.com/stretchr/testify/assert"
"math/big"
"testing"
"time"
)

var testJsonOrgGroupData = "{ \"org_name\": \"organization_name\", \"org_id\": \"org_id1\", \"groups\": [ { \"group_name\": \"default_group2\", \"group_id\": \"99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=\", \"payment\": { \"payment_address\": \"0x671276c61943A35D5F230d076bDFd91B0c47bF09\", \"payment_expiration_threshold\": 40320, \"payment_channel_storage_type\": \"etcd\", \"payment_channel_storage_client\": { \"connection_timeout\": \"15s\", \"request_timeout\": \"13s\", \"endpoints\": [ \"http://127.0.0.1:2379\" ] } } }, { \"group_name\": \"default_group\", \"group_id\": \"99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=\", \"payment\": { \"payment_address\": \"0x671276c61943A35D5F230d076bDFd91B0c47bF09\", \"payment_expiration_threshold\": 40320, \"payment_channel_storage_type\": \"etcd\", \"payment_channel_storage_client\": { \"connection_timeout\": \"15s\", \"request_timeout\": \"13s\", \"endpoints\": [ \"http://127.0.0.1:2379\" ] } } } ] }"

func TestGetOrganizationMetaData(t *testing.T) {
metadata, err := InitOrganizationMetaDataFromJson(testJsonOrgGroupData)
assert.Nil(t, err)
assert.NotNil(t, metadata)
assert.Equal(t, "organization_name", metadata.OrgName)
address := metadata.GetPaymentAddress()
assert.Equal(t, "0x671276c61943A35D5F230d076bDFd91B0c47bF09", address.Hex())
assert.Equal(t, "http://127.0.0.1:2379", metadata.GetPaymentStorageEndPoints()[0])
assert.Equal(t, "99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=", metadata.GetGroupIdString())
assert.Equal(t, big.NewInt(40320), metadata.GetPaymentExpirationThreshold())
grpId, _ := ConvertBase64Encoding("99ybRIg2wAx55mqVsA6sB4S7WxPQHNKqa4BPu/bhj+U=")
assert.Equal(t, grpId, metadata.GetGroupId())
assert.Equal(t, 15*time.Second, metadata.GetConnectionTimeOut())
assert.Equal(t, 13*time.Second, metadata.GetRequestTimeOut())

}

func TestGetOrganizationMetaDataForError(t *testing.T) {
metadata, err := InitOrganizationMetaDataFromJson("bad json")
assert.Nil(t, metadata)
assert.NotNil(t, err)

config.Vip().Set(config.DaemonGroupName, "unknow")
if metadata, err = InitOrganizationMetaDataFromJson(testJsonOrgGroupData); err != nil {
assert.Nil(t, metadata)
assert.Equal(t, "group name unknow in config is invalid, there was no group found with this name in the metadata", err.Error())
}
config.Vip().Set(config.DaemonGroupName, "default_group")
}
Loading

0 comments on commit b3328fe

Please sign in to comment.