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
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
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
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
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
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