Running in CLI

Hardware Requirements

Minimum:

  • CPU with 1+ cores

  • 2GB RAM

  • 4 MBit/sec download Internet service

Recommended:

  • Fast CPU with 2+ cores

  • 4GB+ RAM

  • 8+ MBit/sec download Internet service

The verifier node does not need a static IP address or open ports.

The verifier node does not require significant storage capacity. The minimum storage needed is merely for the software and its configuration, which should be less than 100MB.

Running in Desktop App

You can operate your nodes on any OS by using the Desktop App. Check Running in Desktop Appfor more details.

Starting with Source Code

If you prefer to start with the source code, you can choose one of the following methods:

Using Source Code

Note that the configuration may change while testing. Please check this page consistently for operation info.

Before you start

The verifier is written in Golang. Before you start, you should make sure you have Golang installed correctly. For more details check: https://go.dev/doc/install.

Clone the source code

The verifier code is open source and currently in development. You can clone the GitHub repository and build the source: https://github.com/Bluwhale-protocol/verifier.

Copy

git clone https://github.com/Bluwhale-protocol/verifier.git && \

cd verifier && \

git checkout verifier-alphanet && \

make build

Run via binary program

After executing make build or make all, the verifier executable file will be compiled into the ./bin directory. You need to switch to the ./bin directory before executing verifier. run

Copy

cd bin

If this is your first time running verifier, you need to specify a private key. The private key will sign the verification transaction.

Never expose your main account's private key. Always generate a brand new private key for operating the node, otherwise generate a new one using the `generate-keystore` command below.

The private key can be passed to the verifier through startup parameters, or written into the configuration file.

Through startup parameters

If you already have a brand new private key generated, you can start your node with the command below.

The private key can be any brand new private key. If the key doesn't hold the Bluwhale node license, you need to go to https://alphanet-explorer.Bluwhale.io/verifiers (live on July 1st) to delegate your license to this new key.

Commission rate can be set between 0-100. 0 means you don't take any commission from delegators. 100 means you take all rewards.

Copy

# Pass the pre-generated private key in clear text

./verifier -private-key <Your Private Key> -reward-address <Your Reward Address> -commission-rate <Your Commission Rate>

# By specifying keystore

./verifier -keystore-path <Path to keystore file> -keystore-password <keystore password> -reward-address <Your Reward Address> -commission-rate <Your Commission Rate>

In order to facilitate user operation, verifier provides a tool to generate a new keystore, run

Copy

./verifier -generate-keystore -keystore-path <path to generate your keystore file>

After running the command, you will be prompted to enter a password for the keystore. After entering the password, the keystore file will be generated in the specified path. And then you can run the verifier with the keystore file path and password before you delegate to the keystore address.

Same for Keystore. Before you run the verifier, you must delegate to the keystore address. After the delegation is successful, you can run the verifier again. Delegated address you can get from the terminal after generating the keystore. You can delegate your License through alphanet explorer https://alphanet-explorer.Bluwhale.io/verifiers (live on July 1st).

Through configuration file

If you prefer to config your own parameters, including RPC url, private key mode, etc. you can operate your node using config file, check details below:

  • Set wallet.mode in the configuration file (../configs/config.yaml) to 1,

  • Write the plain text private key into wallet.private_key,

  • Write your reward address into wallet.reward_claimer_addr,

  • Write your commission rate into wallet.commission_rate. Then run

Copy

./verifier -conf ../configs/config.yaml

Configure the path and password of the keystore:

  • Set wallet.mode in the configuration file (../configs/config.yaml) to 2,

  • Write the path and password of the keystore file into wallet.keystore_path

  • Write the keystore password into wallet.keystore_password.

  • Write your reward address into wallet.reward_claimer_addr,

  • Write your commission rate into wallet.commission_rate. Then run

Copy

./verifier -conf ../configs/config.yaml

Using Docker

Note that the configuration may change while testing. Please check this page consistently for operation info.

One of the quickest ways to run verifier is by using Docker:

Use Private Key (mode = 0)

Update config_docker.yaml

Update your private_key and run :

Copy

wallet:

# wallet mode, by which way to pass the private key

# 0: through startup parameters

# 1: through plain text private key in config file

# 2: through path and password of the keystore in config file

mode: 0

# plain text private key, needed when mode is 1

private_key: "99a038e9d345d0b12130b3b1fb003bf8f2d3a5c27ce2a800bbb1608efff6c591"

# path of the keystore, needed when mode is 2

keystore_path: ""

# password of the keystore, needed when mode is 2

keystore_password: ""

Copy

docker run -d --name verifier -v /<Path To This Repository>/verifier/configs:/data/conf -v /<Path To Keystore direction>:/data/keystore Bluwhaleprotocol/verifier:alphanet

Use Keystore (mode = 2)

If you want to use keystore, you need to generate a keystore file first. You can use the following command to generate a keystore file.

Copy

./verifier -generate-keystore -keystore-path <path to generate your keystore file>

After running the command, you can run verifier by following the steps below.

update config_docker.yaml

Update your keystore_path and keystore_password and run :

Copy

wallet:

# wallet mode, by which way to pass the private key

# 0: through startup parameters

# 1: through plain text private key in config file

# 2: through path and password of the keystore in config file

mode: 2

# plain text private key, needed when mode is 1

private_key: ""

# path of the keystore, needed when mode is 2

keystore_path: "/data/conf/xxxx/UTC--2021-09-29T07-00-00.000000000Z--xxxx"

# password of the keystore, needed when mode is 2

keystore_password: "123456"

Copy

docker run -d --name verifier -v /<Path To This Repository>/verifier/configs:/data/conf -v /<Path To Keystore direction>:/data/keystore Bluwhaleprotocol/verifier:alphanet

Example Config

Copy

```yaml

chain:

chain_id: 42161

chain_name: "arbitrum"

rpc_url: "https://arb1.arbitrum.io/rpc"

start_block: 0

offset_block: 14400 # arbitrum block time: 0.25 sec. An offset of 14400 starts fetching blocks from 1 hours ago.

contract:

addr: "0xd35AF24099f6BAd4690eD6E858273580e2b1954A"

tee_addr : "0x7f57004E08ef1702b2b88b87ae01a561ae10F10e"

topic1: "0x89a3b784b99180438f3b2027aa89e97c3c3ed66e8dc78a555d7013b39caf1a89"

topic2: "0x455929120054502ca2ea8194b26e7bb3acb631d30177f6881ffa70581abd4a13"

#wallet:

## wallet mode, by which way to pass the private key

## 0: through startup parameters

## 1: through plain text private key in config file

## 2: through path and password of the keystore in config file

# mode: 1

# private_key: "99a038e9d345d0b12130b3b1fb003bf8f2d3a5c27ce2a800bbb1608efff6c591"

# keystore_path: ""

# keystore_password: ""

# reward_claimer_addr: "0x689d0b32Da0480095b7AE7b604f1b1997736B3F9"

# commission_rate: 100

wallet:

mode: 2

private_key: ""

keystore_path: "./keystore/UTC--2024-06-25T12-01-01.663731000Z--031cff11b035aa5f5189f163c1fc937bf0be235c"

keystore_password: "123456"

reward_claimer_addr: "0x689d0b32Da0480095b7AE7b604f1b1997736B3F9"

commission_rate: 100

signature:

domain_name: "ProtocolService"

domain_version: "1.0.0"

expired_time: 3600

gasless_service:

url: "https://interface.Bluwhale.io"

Gasless Server API

To minimize node operating costs, Bluwhale offers official endpoints that assist node operators in batching operations and posting them on-chain. While gasless transactions still necessitate user signatures for specific operations, Bluwhale ensures endpoint availability. However, if you are concerned about risks, you also have the option to independently send transactions to the smart contract.

Base URLs: https://interface.Bluwhale.io

Message Construction

To make sure the gasless server still guarantee the authorization from the original node operater, it adopts the EIP-712 standard to proof authorization through the signature.

For more details of message structure: https://github.com/Bluwhale-protocol/verifier/blob/verifier-alphanet/internal/worker/signature.server.go. Example config: Example Config

POST ExplorerSendTxNodeEnter

POST /explorer_alphanet/send_tx_node_enter

Message structure (Golang)

Copy

typedData := apitypes.TypedData{

Types: apitypes.Types{

"EIP712Domain": {

{

Name: "name",

Type: "string",

},

{

Name: "version",

Type: "string",

},

{

Name: "chainId",

Type: "uint256",

},

},

"NodeEnterData": {

{

Name: "replacedNode",

Type: "address",

},

{

Name: "expiredAt",

Type: "uint256",

},

},

},

PrimaryType: "NodeEnterData",

Domain: apitypes.TypedDataDomain{

Name: c.cf.Signature.DomainName,

Version: c.cf.Signature.DomainVersion,

ChainId: (*math.HexOrDecimal256)(big.NewInt(c.cf.Chain.ChainId)),

},

Message: apitypes.TypedDataMessage{

"replacedNode": replacedNode.String(),

"expiredAt": expiredAt,

},

}

Body Parameters

Copy

{

"signer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"replaced_node": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"expired_at": 1000000000,

"v": 27,

"r": "ac0b5874f37c40838a33663da72cf90629a0164f98d7785736cc3fd96abeec67",

"s": "10ba15d823cd831ef4061d474cc42d66933c6e45c2865b6d9c5f3646a2599b55"

}

Params

Name

Location

Type

Required

Description

Origin

header

string

yes

User-Agent

header

string

yes

x-app-id

header

string

yes

» signer

body

string

yes

» replaced_node

body

string

yes

The node to replace. There are 2000 active node limit. To join the active set you have to specify a node has lower delegation than you to replace with.

» expired_at

body

integer

yes

» v

body

integer

yes

» r

body

string

yes

» s

body

string

yes

Response Examples

200 Response

Copy

{

"code": 0,

"msg": "string",

"data": {}

}

Responses

HTTP Status Code

Meaning

Description

Data schema

200

Success

Inline

400

ResponseForFail

Inline

Responses Data Schema

HTTP Status Code 200

Name

Type

Required

» code

integer

true

» msg

string

true

» data

object

true

HTTP Status Code 400

Name

Type

Required

» error

string

true

POST ExplorerSendTxNodeExit

POST /explorer_alphanet/send_tx_node_exit

Message structure (Golang)

Copy

apitypes.TypedData{

Types: apitypes.Types{

"EIP712Domain": {

{

Name: "name",

Type: "string",

},

{

Name: "version",

Type: "string",

},

{

Name: "chainId",

Type: "uint256",

},

},

"NodeExitData": {

{

Name: "expiredAt",

Type: "uint256",

},

},

},

PrimaryType: "NodeExitData",

Domain: apitypes.TypedDataDomain{

Name: c.cf.Signature.DomainName,

Version: c.cf.Signature.DomainVersion,

ChainId: (*math.HexOrDecimal256)(big.NewInt(c.cf.Chain.ChainId)),

},

Message: apitypes.TypedDataMessage{

"expiredAt": expiredAt,

},

}

v, r, s, err := tools.SignTypedDataAndSplit(typedData, c.verifierPrivKey)

if err != nil {

return

}

}

Body Parameters

Copy

{

"signer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"expired_at": 1000000000,

"v": 27,

"r": "ac0b5874f37c40838a33663da72cf90629a0164f98d7785736cc3fd96abeec67",

"s": "10ba15d823cd831ef4061d474cc42d66933c6e45c2865b6d9c5f3646a2599b55"

}

Params

Name

Location

Type

Required

Origin

header

string

yes

User-Agent

header

string

yes

x-app-id

header

string

yes

» signer

body

string

yes

» expired_at

body

integer

yes

» v

body

integer

yes

» r

body

string

yes

» s

body

string

yes

Response Examples

200 Response

Copy

{

"code": 0,

"msg": "string",

"data": {}

}

Responses

HTTP Status Code

Meaning

Description

Data schema

200

Success

Inline

400

ResponseForFail

Inline

Responses Data Schema

HTTP Status Code 200

Name

Type

Required

» code

integer

true

» msg

string

true

» data

object

true

HTTP Status Code 400

Name

Type

Required

» error

string

true

POST ExplorerSendTxModifyCommissionRate

POST /explorer_alphanet/send_tx_modify_commission_rate

Message structure (Golang)

Copy

typedData := apitypes.TypedData{

Types: apitypes.Types{

"EIP712Domain": {

{

Name: "name",

Type: "string",

},

{

Name: "version",

Type: "string",

},

{

Name: "chainId",

Type: "uint256",

},

},

"NodeModifyCommissionRateData": {

{

Name: "commissionRate",

Type: "uint32",

},

{

Name: "expiredAt",

Type: "uint256",

},

},

},

PrimaryType: "NodeModifyCommissionRateData",

Domain: apitypes.TypedDataDomain{

Name: c.cf.Signature.DomainName,

Version: c.cf.Signature.DomainVersion,

ChainId: (*math.HexOrDecimal256)(big.NewInt(c.cf.Chain.ChainId)),

},

Message: apitypes.TypedDataMessage{

"commissionRate": strconv.Itoa(int(commissionRate)),

"expiredAt": expiredAt,

},

}

Body Parameters

Copy

{

"signer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"commission_rate": 100,

"expired_at": 1000000000,

"v": 27,

"r": "ac0b5874f37c40838a33663da72cf90629a0164f98d7785736cc3fd96abeec67",

"s": "10ba15d823cd831ef4061d474cc42d66933c6e45c2865b6d9c5f3646a2599b55"

}

Params

Name

Location

Type

Required

Origin

header

string

yes

User-Agent

header

string

yes

x-app-id

header

string

yes

» signer

body

string

yes

» commission_rate

body

integer

yes

» expired_at

body

integer

yes

» v

body

integer

yes

» r

body

string

yes

» s

body

string

yes

Response Examples

200 Response

Copy

{

"code": 0,

"msg": "string",

"data": {}

}

Responses

HTTP Status Code

Meaning

Description

Data schema

200

Success

Inline

400

ResponseForFail

Inline

Responses Data Schema

HTTP Status Code 200

Name

Type

Required

» code

integer

true

» msg

string

true

» data

object

true

HTTP Status Code 400

Name

Type

Required

» error

string

true

POST ExplorerSendTxSetRewardClaimer

POST /explorer_alphanet/send_tx_set_reward_claimer

Message structure (Golang)

Copy

typedData := apitypes.TypedData{

Types: apitypes.Types{

"EIP712Domain": {

{

Name: "name",

Type: "string",

},

{

Name: "version",

Type: "string",

},

{

Name: "chainId",

Type: "uint256",

},

},

"NodeSetRewardClaimerData": {

{

Name: "claimer",

Type: "address",

},

{

Name: "expiredAt",

Type: "uint256",

},

},

},

PrimaryType: "NodeSetRewardClaimerData",

Domain: apitypes.TypedDataDomain{

Name: c.cf.Signature.DomainName,

Version: c.cf.Signature.DomainVersion,

ChainId: (*math.HexOrDecimal256)(big.NewInt(c.cf.Chain.ChainId)),

},

Message: apitypes.TypedDataMessage{

"claimer": rewardClaimer.String(),

"expiredAt": expiredAt,

},

}

Body Parameters

Copy

{

"signer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"claimer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"expired_at": 1000000000,

"v": 27,

"r": "ac0b5874f37c40838a33663da72cf90629a0164f98d7785736cc3fd96abeec67",

"s": "10ba15d823cd831ef4061d474cc42d66933c6e45c2865b6d9c5f3646a2599b55"

}

Params

Name

Location

Type

Required

Origin

header

string

yes

User-Agent

header

string

yes

x-app-id

header

string

yes

body

body

object

no

» signer

body

string

yes

» claimer

body

string

yes

» expired_at

body

integer

yes

» v

body

integer

yes

» r

body

string

yes

» s

body

string

yes

Response Examples

200 Response

Copy

{

"code": 0,

"msg": "string",

"data": {}

}

Responses

HTTP Status Code

Meaning

Description

Data schema

200

Success

Inline

400

ResponseForFail

Inline

Responses Data Schema

HTTP Status Code 200

Name

Type

Required

» code

integer

true

» msg

string

true

» data

object

true

HTTP Status Code 400

Name

Type

Required

» error

string

true

POST ExplorerSendTxNodeReportVerification

POST /explorer_alphanet/send_tx_node_report_verification

Message structure (Golang)

Copy

typedData := apitypes.TypedData{

Types: apitypes.Types{

"EIP712Domain": {

{

Name: "name",

Type: "string",

},

{

Name: "version",

Type: "string",

},

{

Name: "chainId",

Type: "uint256",

},

},

"VerificationData": {

{

Name: "attestationID",

Type: "bytes32",

},

{

Name: "result",

Type: "uint8",

},

{

Name: "index",

Type: "uint32",

},

},

},

PrimaryType: "VerificationData",

Domain: apitypes.TypedDataDomain{

Name: c.cf.Signature.DomainName,

Version: c.cf.Signature.DomainVersion,

ChainId: (*math.HexOrDecimal256)(big.NewInt(c.cf.Chain.ChainId)),

},

Message: apitypes.TypedDataMessage{

"attestationID": attestationId[:],

"result": strconv.Itoa(int(result)),

"index": strconv.Itoa(int(index)),

},

}

Body Parameters

Copy

{

"signer": "0xb1878c4d1BAAbbB6abba3d77836cC85A80D5753B",

"attestation_id": "0x518c1c43067238438f81546f39623c49b09a8eeeb0ee14794aafefd9fa84c7ab",

"result": 0,

"index": 1,

"v": 27,

"r": "ac0b5874f37c40838a33663da72cf90629a0164f98d7785736cc3fd96abeec67",

"s": "10ba15d823cd831ef4061d474cc42d66933c6e45c2865b6d9c5f3646a2599b55"

}

Params

Name

Location

Type

Required

Origin

header

string

yes

User-Agent

header

string

yes

x-app-id

header

string

yes

» signer

body

string

yes

» attestation_id

body

string

yes

» result

body

integer

yes

» index

body

integer

yes

» v

body

integer

yes

» r

body

string

yes

» s

body

string

yes

Response Examples

200 Response

Copy

{

"code": 0,

"msg": "string",

"data": null

}

Responses

HTTP Status Code

Meaning

Description

Data schema

200

Success

Inline

400

ResponseForFail

Inline

Responses Data Schema

HTTP Status Code 200

Name

Type

Required

» code

integer

true

» msg

string

true

» data

null

true

HTTP Status Code 400

Name

Type

Required

» error

string

true

Last updated