Add Network to MetaMask, one-click by Chainlist
*-------------------------*-----------------------------------------------------------------------*
| PRIMARY P-CHAIN ADDRESS | P-fuji1lcztar3x7ra0ajen3dtw4mdhk2cyshfhu2hzgk |
*-------------------------*-----------------------------------------------------------------------*
| TOTAL P-CHAIN BALANCE | 2.0910000 $AVAX |
*-------------------------*-----------------------------------------------------------------------*
| URI | https://api.avax-test.network |
*-------------------------*-----------------------------------------------------------------------*
| NETWORK NAME | fuji |
*-------------------------*-----------------------------------------------------------------------*
| SUBNET VALIDATORS | [24WK7qiKXAumya1kKEktwj2ubBbRyq5UW A2Z8m7egVLhKf1Qj14uvXadhExM5zrB7p] |
*-------------------------*-----------------------------------------------------------------------*
| SUBNET ID | 81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe |
*-------------------------*-----------------------------------------------------------------------*
| BLOCKCHAIN ID | 2oo5UvYgFQikM7KBsMXFQE3RQv3xAFFc8JY2GEBNBF1tp4JaeZ |
*-------------------------*-----------------------------------------------------------------------*
| CHAIN NAME | captevm |
*-------------------------*-----------------------------------------------------------------------*
| VM ID | kmYb53NrmqcW7gfV2FGHBHWXNA6YhhWf7R7LoQeGj9mdDYuaT |
*-------------------------*-----------------------------------------------------------------------*
| VM GENESIS PATH | ../genesis/genesis-nativecoin-feemgr-feerecv.json |
*-------------------------*-----------------------------------------------------------------------*
Environment
Add Network to MetaMask, one-click by Chainlist
*-------------------------*----------------------------------------------------*
| PRIMARY P-CHAIN ADDRESS | P-avax142ue2exu7qxuawxe34ww8t623lv82tu2vt573g |
*-------------------------*----------------------------------------------------*
| TOTAL P-CHAIN BALANCE | 6.9990000 $AVAX |
*-------------------------*----------------------------------------------------*
| URI | https://api.avax.network |
*-------------------------*----------------------------------------------------*
| NETWORK NAME | mainnet |
*-------------------------*----------------------------------------------------*
| SUBNET VALIDATORS | [BXTBUqX8gitUDtVam4fhRWGD1SfeHGoBx] |
*-------------------------*----------------------------------------------------*
| SUBNET ID | 2gHgAgyDHQv7jzFg6MxU2yyKq5NZBpwFLFeP8xX2E3gyK1SzSQ |
*-------------------------*----------------------------------------------------*
| BLOCKCHAIN ID | 2PDRxzc6jMbZSTLb3sufkVszgQc2jtDnYZGtDTAAfom1CTwPsE |
*-------------------------*----------------------------------------------------*
| CHAIN NAME | numbersevm |
*-------------------------*----------------------------------------------------*
| VM ID | qeX7kcVMMkVLB9ZJKTpvtSjpLbtYooNEdpFzFShwRTFu76qdx |
*-------------------------*----------------------------------------------------*
| VM GENESIS PATH | ./genesis.json |
*-------------------------*----------------------------------------------------*
Environment
0x0100000000000000000000000000000000000000
, it is unchangeable.Check the official doc for launching a node (including AWS/GCP).
You can set optional custom configs by the --chain-config-dir
parameter, and the default directory path is $HOME/.avalanchego/configs/chains/
.
References
Create subnet by subnet-cli
.
subnet-cli
uses the key specified in file $PWD/.subnet-cli.pk
on the P-Chain to pay for the transaction fee.subnet-cli
uses Fuji by default. To use mainnet, add --public-uri https://api.avax.network
(details).Check blockchain information by platform.getBlockchains
:
{
"id": "29YUizsFS9pFPvYjsSKB23M2QAooU1yCE2dfSw6pBhpL46SA18",
"name": "captevm",
"subnetID": "2fQBahhq3F9eip8KobMgjbvBEahW3153kvAy6YPDrGMTceZcGG",
"vmID": "kmYb53NrmqcW7gfV2FGHBHWXNA6YhhWf7R7LoQeGj9mdDYuaT"
}
Add validator to subnet
Launch validator. When running avalanchego
, add
-—http-host=0.0.0.0
: Make MetaMask can access the RPC URL--http-allowed-hosts="*"
: Allow traffic from the RPC node (since v1.10.3)./avalanchego \
--track-subnets=81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe\
--network-id=fuji \
--http-host=0.0.0.0 \
--http-allowed-hosts="*" \
--public-ip=<node-public-ip>
High-level concepts
Add the validator to the subnet
Ues subnet admin to run subnet-cli
. Find the Subnet admin in the Subnets section below.
subnet-cli
on your notebook instead of validator nodes.avalanchego-api-scripts/subnet-cli/install-subnet-cli-testnet.sh
to install.Run the command
# testnet
subnet-cli add subnet-validator \
--node-ids="NodeID-7TwAjiRpTbNcqUx6F9EoyXRBLAfeoQXRq" \
--subnet-id="81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe"
# mainnet
subnet-cli add subnet-validator \
--node-ids="NodeID-BXTBUqX8gitUDtVam4fhRWGD1SfeHGoBx" \
--subnet-id="2gHgAgyDHQv7jzFg6MxU2yyKq5NZBpwFLFeP8xX2E3gyK1SzSQ" \
--public-uri "https://api.avax.network"
subnet-cli
will find private key from $PWD/.subnet-cli.pk
by default.
The wallet needs to have 0.001 AVAX at least on P-Chain (Fuji).
If you see Error: validator not found
when adding a validator to subnet and confirmed that you've re-staked successfully, you can check if the node is running as P-Chain validator by checking the node's explorer page: https://subnets-test.avax.network/validators/NodeID-<node-id>
. After re-staking, P-Chain needs minutes to update the validator status.
If the validator's staking duration is expired, the staking amount and rewards will be sent to the staking wallet on P-Chain automatically. You can follow the same steps above to add it as a validator again.
Before validation staking expires, any wallet can not stake to a validator again. If you try to do so on wallet.avax.network, you will get the error message
couldn't issue tx: attempted to issue duplicate validation for NodeID-A2Z8m7egVLhKf1Qj14uvXadhExM5zrB7p
Validator version distributions: mainnet, testnet
Renew Numbers Validators (internal task)
If you're asked to provide BLS Public Key and BLS Signature, you can get them by running the following command on the validator node:
curl -X POST --data '{
"jsonrpc":"2.0",
"id" :1,
"method" :"info.getNodeID"
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info
It should return something like this.
{
"jsonrpc": "2.0",
"result": {
"nodeID": "NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD",
"nodePOP": {
"publicKey": "0x8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e15",
"proofOfPossession": "0x86a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98"
}
},
"id": 1
}
The publicKey is your BLS Public Key, and proofOfPossession is your BLS Signature. Avalanche uses your public key as the signing message (source 1, source 2).
MetaMask RPC rule: http://NodeIPAddress:9650/ext/bc/BlockchainID/rpc
Use Nginx as RPC load balancer.
$ vim /etc/nginx/sites-available/default
Test POST on the RPC node locally:
$ cat check_version.sh
#!/bin/bash
curl -X POST --data '{
"jsonrpc": "2.0",
"id": 10508,
"method": "eth_chainId"
}' -H "Content-Type: application/json" http://localhost
Testing result:
$ ./check_version.sh
{"jsonrpc":"2.0","id":10508,"result":"0x290c"}
Backup node
cd
cp ~/.avalanchego/staking/staker.crt .
cp ~/.avalanchego/staking/staker.key .
Download pre-build avalanchego
and subnet-evm
Copy subnet-evm
binary to avalanchego/plugins/<vmid>
Stop the running avalanchego
Start the new avalanchego
Notes
When upgrading node, you need to upgrade EVM and plugins as well.
jpop32 — 05/12/2021 Plugin is the part of the installation. And it has to be upgraded along with the main executable, yes.
Since avalanchego v1.9.6
, there are two breaking changes
avalanchego/plugins/evm
(Subnet EVM) because it has been merged into avalanchego
directly.~/.avalanchego/plugins/
$ tree avalanchego-v1.9.6
avalanchego-v1.9.6
└── avalanchego
$ tree ~/.avalanchego/plugins/
/home/<account>/.avalanchego/plugins/
└── kmYb53NrmqcW7gfV2FGHBHWXNA6YhhWf7R7LoQeGj9mdDYuaT
~/.avalanchego/plugins/<vmID>
avalanchego
)See chains/update-validator-{mainnet,testnet}.sh
.
Each blockchain has some genesis state when it’s created. Each Virtual Machine defines the format and semantics of its genesis data.
Config directory structure
$ tree ~/.avalanchego/configs/
/home/ubuntu/.avalanchego/configs/
├── chains
│ └── 29YUizsFS9pFPvYjsSKB23M2QAooU1yCE2dfSw6pBhpL46SA18
│ └── config.json
└── subnets
└── 2fQBahhq3F9eip8KobMgjbvBEahW3153kvAy6YPDrGMTceZcGG.json
Tips
config.json
, avalanchego
needs to be restarted.References
> contract = await ethers.getContractAt("NativeMinterInterface", "0x0200000000000000000000000000000000000001")
> [admin] = await ethers.getSigners()
> await admin.getBalance()
BigNumber { value: "99999999999845284000000300" }
> r = await contract.mintNativeCoin(admin.address, 100)
> await admin.getBalance()
BigNumber { value: "99999999999793712000000400" }
Check the official doc for the details.
data
0x4f5aaaba0000000000000000000000008cba0477d89394e6d8ad658e11d52113a2da4ab20000000000000000000000000000000000000000001acff4350c4a1576280000
decode
Signature (Method ID): 0x4f5aaaba
[0]: 0000000000000000000000008cba0477d89394e6d8ad658e11d52113a2da4ab2
[1]: 0000000000000000000000000000000000000000001acff4350c4a1576280000
decode [1], the result is 32414106000000000000000000
(32,414,106 in Wei).
> fm = await ethers.getContractAt("IFeeManager", "0x0200000000000000000000000000000000000003")
> await fm.getFeeConfig()
[
BigNumber { value: "20000000" },
BigNumber { value: "2" },
BigNumber { value: "1000000000" },
BigNumber { value: "100000000" },
BigNumber { value: "48" },
BigNumber { value: "0" },
BigNumber { value: "10000000" },
BigNumber { value: "500000" },
gasLimit: BigNumber { value: "20000000" },
targetBlockRate: BigNumber { value: "2" },
minBaseFee: BigNumber { value: "1000000000" },
targetGas: BigNumber { value: "100000000" },
baseFeeChangeDenominator: BigNumber { value: "48" },
minBlockGasCost: BigNumber { value: "0" },
maxBlockGasCost: BigNumber { value: "10000000" },
blockGasCostStep: BigNumber { value: "500000" }
]
> await fm.setFeeConfig(20000000, 2, 1000000000, 100000000, 48, 0, 10000000, 500000)
> await fm.getFeeConfig()
[
BigNumber { value: "20000000" },
BigNumber { value: "2" },
BigNumber { value: "2000000000" },
BigNumber { value: "100000000" },
BigNumber { value: "48" },
BigNumber { value: "0" },
BigNumber { value: "10000000" },
BigNumber { value: "500000" },
gasLimit: BigNumber { value: "20000000" },
targetBlockRate: BigNumber { value: "2" },
minBaseFee: BigNumber { value: "2000000000" },
targetGas: BigNumber { value: "100000000" },
baseFeeChangeDenominator: BigNumber { value: "48" },
minBlockGasCost: BigNumber { value: "0" },
maxBlockGasCost: BigNumber { value: "10000000" },
blockGasCostStep: BigNumber { value: "500000" }
]
Check the official doc for the details.
> contract = await ethers.getContractAt("IAllowList", "0x0200000000000000000000000000000000000000")
> [admin, test] = await ethers.getSigners()
# 0: None, 1: Enabled, 2: Admin
> await contract.readAllowList(admin.address)
BigNumber { value: "2" }
> await contract.readAllowList(test.address)
BigNumber { value: "0" }
> await contract.setEnabled(test.address)
{
hash: '0xd117e990e85c2428a620f9bd834b0597db1648f3d2fa5899d929fd1701d46e01',
type: 0,
accessList: null,
blockHash: '0xf6063f0c5e86ab4efaf970e901ca3f5be3335ec61ab8f50fc075a6915f5e19e5',
blockNumber: 5,
transactionIndex: 0,
confirmations: 1,
from: '0x8Cba0477d89394E6d8aD658E11d52113A2DA4Ab2',
gasPrice: BigNumber { value: "100000000000" },
gasLimit: BigNumber { value: "41432" },
to: '0x0200000000000000000000000000000000000000',
value: BigNumber { value: "0" },
nonce: 2,
data: '0x0aaf704300000000000000000000000063b7076fc0a914af543c2e5c201df6c29fcc18c5',
r: '0x01e0c663a55757e12237f001811cab7a610c3ebfeba99ac9f0e29cbe4f4bd5ed',
s: '0x38b126e31bb8ee608e45554956c9a6eefb091961755283f9dff69964c2512f41',
v: 21049,
creates: null,
chainId: 10507,
wait: [Function (anonymous)]
}
> await contract.readAllowList(test.address)
BigNumber { value: "1" }
Check the official doc for the details.
Deployers
avalanchego-api-scripts is the admin toolkit.
Node will show "consensus starting" for both C-Chain and P-Chain:
[10-02|06:32:32.114] INFO <C Chain> snowman/transitive.go:401 consensus starting {"lastAcceptedBlock": "2L56FDEYAemMKTJ2uD1MnGpC1pC2WBgeNtwyjh6AhgTffvCWR2"}
[10-02|06:32:32.117] INFO <P Chain> snowman/transitive.go:401 consensus starting {"lastAcceptedBlock": "22egidV1exdWT8ckaAf8dv9YiWAs2ESmY2NmtJUwNqBGCyNef2"}
[10-02|06:32:41.919] INFO <X Chain> avalanche/transitive.go:346 consensus starting {"lenFrontier": 1}
Now, you can check if the Node is running normally.
$ cd ~/avalanchego-api-scripts/api
$ watch -n 5 ./info.isBootstrapped.sh X
$ watch -n 5 ./info.isBootstrapped.sh 2oo5UvYgFQikM7KBsMXFQE3RQv3xAFFc8JY2GEBNBF1tp4JaeZ
$ watch -n 5 ./health.health.sh
# Show validators by giving the Subnet ID
$ ./platform.getCurrentValidators.sh 81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1318 100 1139 100 179 21189 3330 --:--:-- --:--:-- --:--:-- 24867
{
"jsonrpc": "2.0",
"result": {
"validators": [
{
"txID": "QWwjVDJTX2pSYxi5oNk51VeZaZPip9jZnSVhLqipgKMRJDxFP",
"startTime": "1662288001",
"endTime": "1677599993",
"stakeAmount": "1000",
"nodeID": "NodeID-7TwAjiRpTbNcqUx6F9EoyXRBLAfeoQXRq",
"connected": true,
"uptime": "1.0000"
},
{
"txID": "2cfHmfw29UBCjMDTRb9RwistPFekmHzEdfpNci1TLLeoMdtckZ",
"startTime": "1662363248",
"endTime": "1680278397",
"stakeAmount": "1000",
"nodeID": "NodeID-JbeonHKqomERomXgCiXr9oC9vfynkBupj",
"connected": true,
"uptime": "1.0000"
},
{
"txID": "2PqA6BE5wiA5iE9yZeMsfaSag4pT5U1uxMMGyia1HCF6SEUNRe",
"startTime": "1662364392",
"endTime": "1682870344",
"stakeAmount": "1000",
"nodeID": "NodeID-BffXkmzM8EwrBZgpqFp9pwgE9DbDgYKG2",
"connected": true,
"uptime": "1.0000"
},
{
"txID": "26mBFfgiFTz3eFo1QAmvMiBpfTtmgMvzPJ2eB5V6WjK2CkTcuJ",
"startTime": "1667294891",
"endTime": "1685548750",
"stakeAmount": "1000",
"nodeID": "NodeID-24WK7qiKXAumya1kKEktwj2ubBbRyq5UW",
"connected": true,
"uptime": "1.0000"
},
{
"txID": "kApLebvYz54fRratWmWqEttbNktbCDQMK93mDmr1rm9w64gDU",
"startTime": "1669827497",
"endTime": "1688140751",
"stakeAmount": "1000",
"nodeID": "NodeID-A2Z8m7egVLhKf1Qj14uvXadhExM5zrB7p",
"connected": true,
"uptime": "1.0000"
}
]
},
"id": 1
}
Using info.peers
to check all the nodes and versions in a subnet. The nodes might not be validators. The node running the command will not be listed in the result. Remember to add --public-ip=<public-ip>
when running avalanchego
. Check public IPs are correct and uptimes are > 98.
$ ./info.peers.sh | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2027 100 1661 100 366 435k 98255 --:--:-- --:--:-- --:--:-- 659k
{
"jsonrpc": "2.0",
"result": {
"numPeers": "4",
"peers": [
...
{
"ip": "<private-or-public-ip-and-port>",
"publicIP": "private-ip-and-port",
"nodeID": "NodeID-A2Z8m7egVLhKf1Qj14uvXadhExM5zrB7p",
"version": "avalanche/1.9.4",
"lastSent": "2023-01-27T17:03:56Z",
"lastReceived": "2023-01-27T17:03:52Z",
"observedUptime": "99",
"observedSubnetUptimes": {
"81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe": "99"
},
"trackedSubnets": [
"81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe"
],
"benched": []
}
]
},
"id": 1
}
Concept: Nginx with Certbot as reverse proxy redirects RPC requests to the validators.
Concept: Nginx with Certbot as reverse proxy redirects RPC requests to the websocket endpoint of validators.
Websocket log subscription example using Python:
pip3 install websockets
import asyncio
import json
from websockets import connect
async def get_event():
async with connect('wss://testnetrpc.num.network/ws') as ws:
await ws.send('{"jsonrpc":"2.0", "id": 1, "method": "eth_subscribe", "params": ["logs", {"topics": []}]}')
subscription_response = await ws.recv()
print(subscription_response)
while True:
try:
message = await asyncio.wait_for(ws.recv(), timeout=60)
print(json.loads(message))
except asyncio.TimeoutError:
pass
except Exception as e:
print(f'Error: {e}')
break
if __name__ == '__main__':
asyncio.run(get_event())
Create product build.
.env
to build/
where product server.ts is there.Copy build/client/*
to /var/www/html/faucet/
.
Update Nginx config.
+upstream faucet {
+ server localhost:8000
+}
...
- root /var/www/html;
+ root /var/www/html/faucet;
...
+ location /api/ {
+ proxy_pass http://faucet
+ }
Run product server: npm start
Utility commands
$ sudo service nginx configtest
$ sudo service nginx reload
We completed a technical survey and verified some concepts on the testnet.
If you are interested in wrapped ERC20 token, refer to canonical-wnum.
To bridge native NUM to ERC20/BEP20 NUM, you can use XY Finance (audit report).
To know more about NUM token, you can visit the NUM token website.
Archive Node provides full history of the blockchain and does not need to be a validator.
Make a Full Node instance to be an Archive Node instance:
Create an instance from full node image
Increase disk space to 2TB
Delete ~/.avalanchego/staking/*
Update network config
~/.avalanchego/configs/chains/2PDRxzc6jMbZSTLb3sufkVszgQc2jtDnYZGtDTAAfom1CTwPsE/config.json
~/.avalanchego/configs/chains/2oo5UvYgFQikM7KBsMXFQE3RQv3xAFFc8JY2GEBNBF1tp4JaeZ/config.json
{
"pruning-enabled": false,
# newly added content
"eth-apis": [
"eth",
"eth-filter",
"net",
"web3",
"internal-eth",
"internal-blockchain",
"internal-transaction",
"debug-tracer"
]
}
Run an Archive Node by avalanchego
Run an archive node for mainnet
#!/bin/sh
# Subnet IDs
SUBNET_MAINNET="2gHgAgyDHQv7jzFg6MxU2yyKq5NZBpwFLFeP8xX2E3gyK1SzSQ"
./avalanchego \
--track-subnets=${SUBNET_MAINNET} \
--http-host=0.0.0.0 \
--public-ip=<node-public-ip> \
--http-allowed-hosts="*"
Run an archive node for testnet
#!/bin/sh
# Subnet IDs
SUBNET_MAINNET="81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe"
./avalanchego \
--track-subnets=${SUBNET_MAINNET} \
--http-host=0.0.0.0 \
--public-ip=<node-public-ip> \
--http-allowed-hosts="*"
(optional) Test an Archive Node
Run the commands on the Archive Node's instances for testing it's working as Archive Node.
For mainnet
$ curl http://localhost:9650/ext/bc/2PDRxzc6jMbZSTLb3sufkVszgQc2jtDnYZGtDTAAfom1CTwPsE/rpc \
-X POST \
-H "Content-Type: application/json" \
--data '{"method":"debug_traceTransaction","params":["0x9a241e580d29d90d890316559d055c0df5cc7203be43b166d63c51de2218efc8"],"id":1,"jsonrpc":"2.0"}'
For testnet
$ curl http://localhost:9650/ext/bc/2oo5UvYgFQikM7KBsMXFQE3RQv3xAFFc8JY2GEBNBF1tp4JaeZ/rpc \
-X POST \
-H "Content-Type: application/json" \
--data '{"method":"debug_traceTransaction","params":["0x7d2dec6c3e7ce2a387d988a0603ce7de6d487d6aeaf6b58eabdb123161cee0a2"],"id":1,"jsonrpc":"2.0"}'