diff --git a/Makefile b/Makefile index e6384556f..4ca003d84 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ GIT_TAG = $(shell git tag -l --points-at HEAD) THOR_VERSION = $(shell cat cmd/thor/VERSION) DISCO_VERSION = $(shell cat cmd/disco/VERSION) -PACKAGES = `go list ./... | grep -v '/vendor/'` +UNIT_TEST_PACKAGES = `go list ./... | grep -v '/vendor/'` | grep -v 'e2e' MAJOR = $(shell go version | cut -d' ' -f3 | cut -b 3- | cut -d. -f1) MINOR = $(shell go version | cut -d' ' -f3 | cut -b 3- | cut -d. -f2) @@ -13,20 +13,23 @@ export GO111MODULE=on .PHONY: thor disco all clean test -thor:| go_version_check +help: #@ Show a list of available commands + @egrep -h '\s#@\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?#@ "}; {printf "\033[36m %-30s\033[0m %s\n", $$1, $$2}' + +thor:| go_version_check #@ Build the `thor` executable @echo "building $@..." @go build -v -o $(CURDIR)/bin/$@ -ldflags "-X main.version=$(THOR_VERSION) -X main.gitCommit=$(GIT_COMMIT) -X main.gitTag=$(GIT_TAG)" ./cmd/thor @echo "done. executable created at 'bin/$@'" -disco:| go_version_check +disco:| go_version_check #@ Build the `disco` executable @echo "building $@..." @go build -v -o $(CURDIR)/bin/$@ -ldflags "-X main.version=$(DISCO_VERSION) -X main.gitCommit=$(GIT_COMMIT) -X main.gitTag=$(GIT_TAG)" ./cmd/disco @echo "done. executable created at 'bin/$@'" -dep:| go_version_check +dep:| go_version_check #@ Download dependencies @go mod download -go_version_check: +go_version_check: #@ Check go version @if test $(MAJOR) -lt 1; then \ echo "Go 1.16 or higher required"; \ exit 1; \ @@ -37,13 +40,16 @@ go_version_check: fi \ fi -all: thor disco +all: thor disco #@ Build all executables -clean: +clean: #@ Clean the executables -rm -rf \ $(CURDIR)/bin/thor \ -$(CURDIR)/bin/disco +$(CURDIR)/bin/disco + +test:| go_version_check #@ Run unit tests + @go test -cover $(UNIT_TEST_PACKAGES) -test:| go_version_check - @go test -cover $(PACKAGES) +test-e2e:| go_version_check #@ Run end-to-end tests + GOMAXPROCS=1 go test github.com/vechain/thor/v2/tests/e2e diff --git a/tests/e2e/accounts_test.go b/tests/e2e/accounts_test.go new file mode 100644 index 000000000..a23106c6c --- /dev/null +++ b/tests/e2e/accounts_test.go @@ -0,0 +1,24 @@ +package e2e + +import ( + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/vechain/thor/v2/tests/e2e/client" + "github.com/vechain/thor/v2/tests/e2e/network" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAccountBalance(t *testing.T) { + network.StartCompose(t) + + account, err := client.Default.GetAccount("0xf077b491b355E64048cE21E3A6Fc4751eEeA77fa") + + assert.NoError(t, err, "GetAccount()") + + balance, err := account.Balance.MarshalText() + + assert.NoError(t, err, "MarshalText()") + + assert.Equal(t, hexutil.Encode(balance), "0x307831346164663462373332303333346239303030303030") +} diff --git a/tests/e2e/blocks_test.go b/tests/e2e/blocks_test.go new file mode 100644 index 000000000..d6252f1bb --- /dev/null +++ b/tests/e2e/blocks_test.go @@ -0,0 +1,18 @@ +package e2e + +import ( + "github.com/vechain/thor/v2/tests/e2e/client" + "github.com/vechain/thor/v2/tests/e2e/network" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetBlock(t *testing.T) { + network.StartCompose(t) + + block, err := client.Default.GetCompressedBlock(1) + + assert.NoError(t, err, "GetCompressedBlock()") + assert.Greater(t, block.Number, uint32(0), "GetCompressedBlock()") +} diff --git a/tests/e2e/client/thor_client.go b/tests/e2e/client/thor_client.go new file mode 100644 index 000000000..e4e479da9 --- /dev/null +++ b/tests/e2e/client/thor_client.go @@ -0,0 +1,54 @@ +package client + +import ( + "encoding/json" + "github.com/vechain/thor/v2/api/accounts" + "github.com/vechain/thor/v2/api/blocks" + "net/http" + "strconv" +) + +var ( + Default = NewThorClient("http://localhost:8669") + Node1 = NewThorClient("http://localhost:8669") + Node2 = NewThorClient("http://localhost:8679") + Node3 = NewThorClient("http://localhost:8689") +) + +type ThorClient struct { + baseUrl string +} + +func NewThorClient(baseUrl string) *ThorClient { + return &ThorClient{baseUrl: baseUrl} +} + +func (c *ThorClient) GetAccount(address string) (*accounts.Account, error) { + return Get(c, "/accounts/"+address, new(accounts.Account)) +} + +func (c *ThorClient) GetExpandedBlock(number int32) (*blocks.JSONExpandedBlock, error) { + return Get(c, "/blocks/"+strconv.Itoa(int(number))+"?expanded=true", new(blocks.JSONExpandedBlock)) +} + +func (c *ThorClient) GetCompressedBlock(number int32) (*blocks.JSONCollapsedBlock, error) { + return Get(c, "/blocks/"+strconv.Itoa(int(number)), new(blocks.JSONCollapsedBlock)) +} + +func Get[T any](c *ThorClient, endpoint string, v *T) (*T, error) { + + resp, err := http.Get(c.baseUrl + endpoint) + + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(v) + if err != nil { + return nil, err + } + + return v, nil +} diff --git a/tests/e2e/network/config/genesis.json b/tests/e2e/network/config/genesis.json new file mode 100644 index 000000000..7534e906b --- /dev/null +++ b/tests/e2e/network/config/genesis.json @@ -0,0 +1,87 @@ +{ + "launchTime": 1703180212, + "gasLimit": 10000000, + "extraData": "My custom VeChain", + "accounts": [ + { + "address": "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed", + "balance": "0x14ADF4B7320334B9000000", + "energy": 0, + "code": "0x6060604052600256", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + }, + { + "address": "0xf077b491b355e64048ce21e3a6fc4751eeea77fa", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0x435933c8064b4ae76be665428e0307ef2ccfbd68", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0x0f872421dc479f3c11edd89512731814d0598db5", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0xf370940abdbd2583bc80bfc19d19bc216c88ccf0", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0x99602e4bbc0503b8ff4432bb1857f916c3653b85", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0x61e7d0c2b25706be3485980f39a3a994a8207acf", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0x361277d1b27504f36a3b33d3a52d1f8270331b8c", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + }, + { + "address": "0xd7f75a0a1287ab2916848909c8531a0ea9412800", + "balance": "0x14ADF4B7320334B9000000", + "energy": "0x14ADF4B7320334B9000000" + } + ], + "authority": [ + { + "masterAddress": "0x435933c8064b4ae76be665428e0307ef2ccfbd68", + "endorsorAddress": "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed", + "identity": "0x000000000000000068747470733a2f2f636f6e6e65782e76656368612e696e2f" + }, + { + "masterAddress": "0xf370940abdbd2583bc80bfc19d19bc216c88ccf0", + "endorsorAddress": "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed", + "identity": "0x000000000000000068747470733a2f2f656e762e7665636861696e2e6f72672f" + }, + { + "masterAddress": "0x0f872421dc479f3c11edd89512731814d0598db5", + "endorsorAddress": "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed", + "identity": "0x0000000000000068747470733a2f2f617070732e7665636861696e2e6f72672f" + } + ], + "params": { + "rewardRatio": 300000000000000000, + "baseGasPrice": 1000000000000000, + "proposerEndorsement": "0x14ADF4B7320334B9000000", + "executorAddress": "0x0000000000000000000000004578656375746f72" + }, + "executor": { + "approvers": [ + { + "address": "0x199b836d8a57365baccd4f371c1fabb7be77d389", + "identity": "0x00000000000067656e6572616c20707572706f736520626c6f636b636861696e" + } + ] + } +} diff --git a/tests/e2e/network/config/health_check.sh b/tests/e2e/network/config/health_check.sh new file mode 100755 index 000000000..6c30ed39b --- /dev/null +++ b/tests/e2e/network/config/health_check.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Set the URL +url="http://localhost:8669/blocks/best" + +# Make the HTTP request and store the response in a variable +response=$(wget -qO- $url) + +echo $response + +# Extract the value of "number" from the JSON response using grep +number=$(echo $response | grep -o '"number":[^,}]*' | awk -F: '{print $2}' | tr -d '[:space:]') + +# Check if the number is greater than 0 +if [ $number -gt 0 ]; then + echo "Health check passed! Number is greater than 0." + exit 0 +else + echo "Health check failed! Unexpected response or number is not greater than 0:" + echo $response + exit 1 +fi diff --git a/tests/e2e/network/config/start.sh b/tests/e2e/network/config/start.sh new file mode 100755 index 000000000..703cefe16 --- /dev/null +++ b/tests/e2e/network/config/start.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if [[ -n $MASTER_KEY_ADDRESS ]]; then + echo "Starting node with master key address $MASTER_KEY_ADDRESS" + + cp /node/keys/$MASTER_KEY_ADDRESS/master.key /tmp +fi + +BOOTNODE_IP=$(ping -c 1 thor-disco | awk -F'[()]' '/PING/{print $2}') + +echo $BOOTNODE_IP + +thor --config-dir=/tmp --network /node/config/genesis.json --api-addr="0.0.0.0:8669" --api-cors="*" --bootnode "enode://e32e5960781ce0b43d8c2952eeea4b95e286b1bb5f8c1f0c9f09983ba7141d2fdd7dfbec798aefb30dcd8c3b9b7cda8e9a94396a0192bfa54ab285c2cec515ab@$BOOTNODE_IP:55555" diff --git a/tests/e2e/network/docker-compose-base.yaml b/tests/e2e/network/docker-compose-base.yaml new file mode 100644 index 000000000..7b9e82a5a --- /dev/null +++ b/tests/e2e/network/docker-compose-base.yaml @@ -0,0 +1,18 @@ +version: "3.8" + +services: + base-authority-node: + image: ${THOR_IMAGE:-vechain/thor:latest} + entrypoint: "/node/config/start.sh" + volumes: + - type: bind + source: ./config + target: /node/config + - type: bind + source: ./keys + target: /node/keys + healthcheck: + test: ["CMD", "/node/config/health_check.sh"] + interval: 1s + timeout: 5s + retries: 120 diff --git a/tests/e2e/network/docker-compose.yaml b/tests/e2e/network/docker-compose.yaml new file mode 100644 index 000000000..f409e45af --- /dev/null +++ b/tests/e2e/network/docker-compose.yaml @@ -0,0 +1,38 @@ +version: "3.8" + +services: + + thor-disco: + image: ${THOR_IMAGE:-vechain/thor:latest} + container_name: thor-disco + entrypoint: "disco --keyhex=99f0500549792796c14fed62011a51081dc5b5e68fe8bd8a13b86be829c4fd36" + + authority-node-1: + extends: + file: docker-compose-base.yaml + service: base-authority-node + container_name: authority-node-1 + environment: + MASTER_KEY_ADDRESS: "435933c8064b4ae76be665428e0307ef2ccfbd68" + ports: + - "8669:8669" + + authority-node-2: + extends: + file: docker-compose-base.yaml + service: base-authority-node + container_name: authority-node-2 + environment: + MASTER_KEY_ADDRESS: "f370940abdbd2583bc80bfc19d19bc216c88ccf0" + ports: + - "8679:8669" + + authority-node-3: + extends: + file: docker-compose-base.yaml + service: base-authority-node + container_name: authority-node-3 + environment: + MASTER_KEY_ADDRESS: "0f872421dc479f3c11edd89512731814d0598db5" + ports: + - "8689:8669" diff --git a/tests/e2e/network/docker.go b/tests/e2e/network/docker.go new file mode 100644 index 000000000..188e7f7a8 --- /dev/null +++ b/tests/e2e/network/docker.go @@ -0,0 +1,27 @@ +package network + +import ( + "context" + "github.com/stretchr/testify/assert" + "github.com/testcontainers/testcontainers-go/modules/compose" + "testing" +) + +type Network struct { + stack *compose.ComposeStack +} + +// StartCompose starts the docker-compose network and destroys it after the test +func StartCompose(t *testing.T) { + dc, err := compose.NewDockerCompose("network/docker-compose.yaml") + assert.NoError(t, err, "NewDockerComposeAPI()") + + t.Cleanup(func() { + assert.NoError(t, dc.Down(context.Background(), compose.RemoveOrphans(true), compose.RemoveImagesLocal), "compose.Down()") + }) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + assert.NoError(t, dc.Up(ctx, compose.Wait(true)), "compose.Up()") +} diff --git a/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/keystore.json b/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/keystore.json new file mode 100644 index 000000000..a526b0d6e --- /dev/null +++ b/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/keystore.json @@ -0,0 +1,21 @@ +{ + "address": "0f872421dc479f3c11edd89512731814d0598db5", + "id": "33eda5b7-140d-441d-b450-07ca28fb0105", + "version": 3, + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "182b783ea15a49b815042dd3a8bbc80e" + }, + "ciphertext": "2084fac13c2d3b3e45dd743766abcffe5c48e2d8169a895e7891235949fd3506", + "kdf": "scrypt", + "kdfparams": { + "salt": "e5256c7b430ffefda1b1bcbef44af363eddf01ff8dc9112eb97324bfa6d388c6", + "n": 131072, + "dklen": 32, + "p": 1, + "r": 8 + }, + "mac": "b27043563629497c167a756eed7130079e7a4e7c5aa05fde67ed064a14e6f536" + } +} diff --git a/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/master.key b/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/master.key new file mode 100644 index 000000000..6848f24f2 --- /dev/null +++ b/tests/e2e/network/keys/0f872421dc479f3c11edd89512731814d0598db5/master.key @@ -0,0 +1 @@ +f4a1a17039216f535d42ec23732c79943ffb45a089fbb78a14daad0dae93e991 diff --git a/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/keystore.json b/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/keystore.json new file mode 100644 index 000000000..e3d10a8b0 --- /dev/null +++ b/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/keystore.json @@ -0,0 +1,21 @@ +{ + "address": "435933c8064b4ae76be665428e0307ef2ccfbd68", + "id": "278224be-550d-4adc-94c1-817e45f6f5f1", + "version": 3, + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "8873ddb8810d2337d1f01e3270be12f8" + }, + "ciphertext": "04bfcd9dc4eb86d280fe23209fed323b0067e24dbf8e3169c2062d0e998b6891", + "kdf": "scrypt", + "kdfparams": { + "salt": "8102dd0c6cce78e6166bcd7e49fe203c8965287124e9d8609ccb84b0a4f83620", + "n": 131072, + "dklen": 32, + "p": 1, + "r": 8 + }, + "mac": "dc124531f4fca3fe67214cb6cc1442d71989b476eec1d32667cfec810181c1a1" + } +} diff --git a/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/master.key b/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/master.key new file mode 100644 index 000000000..9b14d7aff --- /dev/null +++ b/tests/e2e/network/keys/435933c8064b4ae76be665428e0307ef2ccfbd68/master.key @@ -0,0 +1 @@ +7b067f53d350f1cf20ec13df416b7b73e88a1dc7331bc904b92108b1e76a08b1 diff --git a/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/keystore.json b/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/keystore.json new file mode 100644 index 000000000..a46a0fdf4 --- /dev/null +++ b/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/keystore.json @@ -0,0 +1,21 @@ +{ + "address": "f077b491b355e64048ce21e3a6fc4751eeea77fa", + "id": "7b30e693-a7a5-471b-84d3-f490b4e10349", + "version": 3, + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "263b752452068a012cf6245459b57bed" + }, + "ciphertext": "a5330f79204343725b41d7ba29b625284cf7e638b6eb5386ef05d595530c36dc", + "kdf": "scrypt", + "kdfparams": { + "salt": "95c76063d034643794a8616ff7223353f1976e4895da7c1293432870c18557d7", + "n": 131072, + "dklen": 32, + "p": 1, + "r": 8 + }, + "mac": "cafc9a1195bcf0bb0586d416a92c04d716d2364574520cbf11b08dd4566c5b5e" + } +} diff --git a/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/master.key b/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/master.key new file mode 100644 index 000000000..8c2d9ed90 --- /dev/null +++ b/tests/e2e/network/keys/f077b491b355e64048ce21e3a6fc4751eeea77fa/master.key @@ -0,0 +1 @@ +99f0500549792796c14fed62011a51081dc5b5e68fe8bd8a13b86be829c4fd36 diff --git a/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/keystore.json b/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/keystore.json new file mode 100644 index 000000000..71aeb2254 --- /dev/null +++ b/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/keystore.json @@ -0,0 +1,21 @@ +{ + "address": "f370940abdbd2583bc80bfc19d19bc216c88ccf0", + "id": "507695ed-8aa6-417f-8a67-8c3cadc669b4", + "version": 3, + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "ca4a9ef516f4fb04e9d28864b681d4b2" + }, + "ciphertext": "b07ffd58fbac19ae8968b57d0ad4aaded1931daa1bb5e973a70edfe65e422c19", + "kdf": "scrypt", + "kdfparams": { + "salt": "2cd63ebe7b87b498236a68f856ef02f2b5a723f63305fa92524903054ffd9e39", + "n": 131072, + "dklen": 32, + "p": 1, + "r": 8 + }, + "mac": "4121304545bd976cbe4e8ab7d174597818fd7ef7710f94ae91a5ba1c189d32ae" + } +} diff --git a/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/master.key b/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/master.key new file mode 100644 index 000000000..8d6aae573 --- /dev/null +++ b/tests/e2e/network/keys/f370940abdbd2583bc80bfc19d19bc216c88ccf0/master.key @@ -0,0 +1 @@ +35b5cc144faca7d7f220fca7ad3420090861d5231d80eb23e1013426847371c4