Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Readme file #3

Merged
merged 15 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 158 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,170 @@ Blocks are promptly generated through a dedicated endpoint whenever users initia

## Features

- Implements all mx-chain-proxy-go endpoints.
- Implements all `mx-chain-proxy-go` endpoints.
- Extra endpoints for specific operations.
- Simulates the behavior of a local testnet without a consensus group.


## Endpoints
## API Documentation

`mx-chain-simulator-go` includes all the [proxy endpoints](https://github.com/multiversx/mx-chain-proxy-go#rest-api-endpoints)


Additionally, the simulator offers custom endpoints:
### Additionally, the simulator offers custom endpoints:

- `simulator/generate-blocks/:num`: This endpoint initiates the generation of a specified number of blocks for each shard.
- `simulator/initial-wallets`: This endpoint will return the initial wallets (address and private key hex encoded)
### `POST /simulator/generate-blocks/:num`

This endpoint initiates the generation of a specified number of blocks for each shard.

##### Request
- **Method:** POST
- **Path:** `/simulator/generate-blocks/:num`
- **Parameters:**
- `num` (path parameter): The number of blocks to generate for each shard.

##### Response
- **Status Codes:**
- `200 OK`: Blocks generated successfully.
- `400 Bad Request`: Invalid request parameters.
-
#### Response Body
```json
{
"data": {},
"error": "",
"code": "successful"
}
```

### `GET /simulator/initial-wallets`

This endpoint returns the initial wallets (address and private key hex encoded).

##### Request
- **Method:** GET
- **Path:** `/simulator/initial-wallets`

##### Response
- **Status Codes:**
- `200 OK`: Initial wallets retrieved successfully.

#### Response Body (Example)
```
{
"data": {
"initialWalletWithStake": {
"address": "erd18e5tqg3x2fvh2f3g2747639erk...",
"privateKeyHex": "7ce93f48840c4a67fdcdc97c..."
},
"shardWallets": {
"0": {
"address": "erd1844ch276gqfmhjgj8jjca4akpf...",
"privateKeyHex": "2024e8a0f202ae3536d336c3..."
},
// ... additional wallet entries
}
},
"error": "",
"code": "successful"
}
```


### `POST /simulator/:address/set-state`

This endpoint allows you to set the state at a specific address.

##### Request
- **Method:** POST
- **Path:** `/simulator/address/:address/set-state`
- **Parameters:**
- `address` (path parameter): The address for which the state will be set.

##### Request Body
The request body should be a JSON object representing a map of hex-encoded key-value pairs.

Example:
```
{
"keyHex1": "valueHex1",
"keyHex2": "valueHex2",
// ... additional hex-encoded key-value pairs
}
```


##### Response
- **Status Codes:**
- `200 OK`: State set successfully.
- `404 Bad Request`: Invalid request parameters.

#### Response Body
```json
{
"data": {},
"error": "",
"code": "successful"
}
```


---


## Prerequisites

Before proceeding, ensure you have the following prerequisites:

- Go programming environment set up.
- Git installed.


## Install

Using the `cmd/chainsimulator` package as root, execute the following commands:

- install go dependencies: `go install`
- build executable: `go build -o chainsimulator`


## Launching the chainsimulator

CLI: run `--help` to get the command line parameters

```
./chainsimulator --help
```

Before launching the `chainsimulator` service, it has to be configured so that it runs with the correct configuration.

The **_[config.toml](./cmd/chainsimulator/config/config.toml)_** file:

```toml
[config]
[config.simulator]
# server-port paramter specifies the port of the http server
server-port = 8085
# num-of-shards parameter specifies the number of shard that chain simulator will simulate
num-of-shards = 3
# round-duration-in-milliseconds parameter specifies the duration of a simulated round. The timestamp between two headers will correspond to the round duration but will not reflect real-time
round-duration-in-milliseconds = 6000
# rounds-per-epoch specifies the number of rounds per epoch
rounds-per-epoch = 20
# mx-chain-go-repo will be used to fetch the node configs folder
mx-chain-go-repo = "https://github.com/multiversx/mx-chain-go"
# mx-chain-proxy-go-repo will be used to fetch the proxy configs folder
mx-chain-proxy-go-repo = "https://github.com/multiversx/mx-chain-proxy-go"
[config.logs]
log-file-life-span-in-mb = 1024 # 1GB
log-file-life-span-in-sec = 432000 # 5 days
log-file-prefix = "chain-simulator"
logs-path = "logs"
```


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

## Contribution

Contributions to the mx-chain-simulator-go module are welcomed. Whether you're interested in improving its features,
extending its capabilities, or addressing issues, your contributions can help the
community make the module even more robust.
3 changes: 3 additions & 0 deletions cmd/chainsimulator/config/config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[config]
[config.simulator]
# server-port paramter specifies the port of the http server
server-port = 8085
# num-of-shards parameter specifies the number of shard that chain simulator will simulate
num-of-shards = 3
# round-duration-in-milliseconds parameter specifies the duration of a simulated round. The timestamp between two headers will correspond to the round duration but will not reflect real-time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

round-duration-in-milliseconds = 6000
# rounds-per-epoch specifies the number of rounds per epoch
rounds-per-epoch = 20
Expand Down
94 changes: 94 additions & 0 deletions examples/esdt/issue-fungible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import sys
import time

from multiversx_sdk_network_providers import ProxyNetworkProvider
from multiversx_sdk_network_providers.transactions import TransactionOnNetwork
from multiversx_sdk_core.transaction_factories import TokenManagementTransactionsFactory, TransactionsFactoryConfig
from multiversx_sdk_core import TransactionComputer
from multiversx_sdk_wallet import UserPEM, UserSigner
from pathlib import Path

SIMULATOR_URL = "http://localhost:8085"
GENERATE_BLOCKS_URL = f"{SIMULATOR_URL}/simulator/generate-blocks"


def main():
# create a network provider
provider = ProxyNetworkProvider(SIMULATOR_URL)

pem = UserPEM.from_file(Path("../wallets/wallet.pem"))

# call proxy faucet
address = pem.public_key.to_address("erd")
data = {"receiver": f"{address.to_bech32()}"}
provider.do_post(f"{SIMULATOR_URL}/transaction/send-user-funds", data)

# generate 20 blocks to pass an epoch and the ESDT contract to be enabled
provider.do_post(f"{GENERATE_BLOCKS_URL}/20", {})

# create transaction config and factory
config = TransactionsFactoryConfig(provider.get_network_config().chain_id)
transaction_factory = TokenManagementTransactionsFactory(config)

# create issue transaction
initial_supply = 100000
tx = transaction_factory.create_transaction_for_issuing_fungible(
sender=address,
token_name="tttt",
token_ticker="TTTT",
initial_supply=initial_supply,
num_decimals=1,
can_pause=False,
can_wipe=False,
can_freeze=False,
can_upgrade=False,
can_change_owner=False,
can_add_special_roles=False,
)

# set issue cost and nonce
tx.amount = 5000000000000000000
tx.nonce = provider.get_account(address).nonce

# sign transaction
user_signer = UserSigner(pem.secret_key)
tx_computer = TransactionComputer()
tx.signature = user_signer.sign(tx_computer.compute_bytes_for_signing(tx))

# send transaction
tx_hash = provider.send_transaction(tx)
print(f"generated tx hash: {tx_hash}")
time.sleep(1)

# execute 5 block ( transaction needs to be executed on source, block on source has to be finalized...)
provider.do_post(f"{GENERATE_BLOCKS_URL}/5", {})

# get transaction with status
tx_from_network = provider.get_transaction(tx_hash, with_process_status=True)

# verify transaction status and account balance
if not tx_from_network.status.is_successful():
sys.exit(f"transaction status is not correct, status received->{tx_from_network.status}")

# verify token balance
token_identifier_string = extract_token_identifier(tx_from_network)
amount = provider.get_fungible_token_of_account(address, token_identifier_string)
if amount.balance != initial_supply:
sys.exit(f"amount of token from balance is no equal with the initial supply: actual-{amount.balance}, expected-{initial_supply}")

print("transaction was executed and tokens were created")


def extract_token_identifier(tx: TransactionOnNetwork) -> str:
for event in tx.logs.events:
if event.identifier != "upgradeProperties":
continue

decoded_bytes = bytes.fromhex(event.topics[0].hex())
return decoded_bytes.decode('utf-8')

return ""


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions examples/faucet/faucet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ curl --request POST \
sleep 0.2

# Generate 1 block
curl --request GET \
curl --request POST \
--url ${SIMULATOR_URL}/simulator/generate-blocks/1

api_response=$(curl --request GET --url ${SIMULATOR_URL}/address/${MY_ADDR})
Expand All @@ -22,7 +22,7 @@ api_response=$(curl --request GET --url ${SIMULATOR_URL}/address/${MY_ADDR})
balance=$(echo "$api_response" | jq -r '.data.account.balance')

# Compare the balance with the expected balance
EXPECTED_BALANCE="1000000000000000000"
EXPECTED_BALANCE="10000000000000000000"
if [ "$balance" != "$EXPECTED_BALANCE" ]; then
echo "Error: Balance $balance does not match expected balance $EXPECTED_BALANCE"
exit 1
Expand Down
5 changes: 5 additions & 0 deletions examples/wallets/wallet.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY for erd1l6xt0rqlyzw56a3k8xwwshq2dcjwy3q9cppucvqsmdyw8r98dz3sae0kxl-----
MWFjYmU1YjhhNTg1M2QzOTRhNDQ0NmU4ODUzMDQ5NzdmMmNlOTdhYzk1OWE1NTJm
NjI0YjBlOGRiM2M2NjZmMmZlOGNiNzhjMWYyMDlkNGQ3NjM2Mzk5Y2U4NWMwYTZl
MjRlMjQ0MDVjMDQzY2MzMDEwZGI0OGUzOGNhNzY4YTM=
-----END PRIVATE KEY for erd1l6xt0rqlyzw56a3k8xwwshq2dcjwy3q9cppucvqsmdyw8r98dz3sae0kxl-----
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.20
require (
github.com/gin-gonic/gin v1.9.1
github.com/multiversx/mx-chain-core-go v1.2.18
github.com/multiversx/mx-chain-go v1.6.4-0.20231115131018-5d3fbefc1db3
github.com/multiversx/mx-chain-go v1.6.4-0.20231121095214-0d8d49b94086
github.com/multiversx/mx-chain-logger-go v1.0.13
github.com/multiversx/mx-chain-proxy-go v1.1.41
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,8 @@ github.com/multiversx/mx-chain-crypto-go v1.2.9 h1:OEfF2kOQrtzUl273Z3DEcshjlTVUf
github.com/multiversx/mx-chain-crypto-go v1.2.9/go.mod h1:fkaWKp1rbQN9wPKya5jeoRyC+c/SyN/NfggreyeBw+8=
github.com/multiversx/mx-chain-es-indexer-go v1.4.13 h1:3Ayaw9bSpeNOF+Z3L/11MN1rIJH8Rc6dqtt+o4Wfdno=
github.com/multiversx/mx-chain-es-indexer-go v1.4.13/go.mod h1:g0REyU8rqJfoBq6mIfqEi6IdpLofECLEvKKGMJO8ZhM=
github.com/multiversx/mx-chain-go v1.6.4-0.20231115131018-5d3fbefc1db3 h1:u/xo0lRnm7sPobTAR01OvracjiJxON0vyZhExcwurTc=
github.com/multiversx/mx-chain-go v1.6.4-0.20231115131018-5d3fbefc1db3/go.mod h1:1Vt+2H+Sx0Ig4oElq703pEGHs4cF8Cg59CG6PDtaxig=
github.com/multiversx/mx-chain-go v1.6.4-0.20231121095214-0d8d49b94086 h1:Um8Ykjtb0UAZJK/FV4hNArywKg1OpaeFSX8AOzeeTaU=
github.com/multiversx/mx-chain-go v1.6.4-0.20231121095214-0d8d49b94086/go.mod h1:1Vt+2H+Sx0Ig4oElq703pEGHs4cF8Cg59CG6PDtaxig=
github.com/multiversx/mx-chain-logger-go v1.0.13 h1:eru/TETo0MkO4ZTnXsQDKf4PBRpAXmqjT02klNT/JnY=
github.com/multiversx/mx-chain-logger-go v1.0.13/go.mod h1:MZJhTAtZTJxT+yK2EHc4ZW3YOHUc1UdjCD0iahRNBZk=
github.com/multiversx/mx-chain-proxy-go v1.1.41 h1:u5LTek2keNvd25jrOmHLp0N4AFwYFImBnrs+GR7IGRY=
Expand Down
1 change: 1 addition & 0 deletions pkg/facade/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos"
type SimulatorHandler interface {
GetInitialWalletKeys() *dtos.InitialWalletKeys
GenerateBlocks(numOfBlocks int) error
SetState(address string, keyValueMap map[string]string) error
IsInterfaceNil() bool
}
5 changes: 5 additions & 0 deletions pkg/facade/simulatorFacade.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func (sf *simulatorFacade) GetInitialWalletKeys() *dtos.InitialWalletKeys {
return sf.simulator.GetInitialWalletKeys()
}

// SetState will set the provided state for an address
func (sf *simulatorFacade) SetState(address string, keyValueMap map[string]string) error {
return sf.simulator.SetState(address, keyValueMap)
}

// IsInterfaceNil returns true if there is no value under the interface
func (sf *simulatorFacade) IsInterfaceNil() bool {
return sf == nil
Expand Down
28 changes: 27 additions & 1 deletion pkg/proxy/api/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"errors"
"fmt"
"net/http"
"strconv"

Expand All @@ -13,6 +14,7 @@ import (
const (
generateBlocksEndpoint = "/simulator/generate-blocks/:num"
initialWalletsEndpoint = "/simulator/initial-wallets"
setKeyValuesEndpoint = "/simulator/address/:address/set-state"
)

type endpointsProcessor struct {
Expand All @@ -33,8 +35,9 @@ func (ep *endpointsProcessor) ExtendProxyServer(httpServer *http.Server) error {
return errors.New("cannot cast httpServer.Handler to gin.Engine")
}

ws.GET(generateBlocksEndpoint, ep.generateBlocks)
ws.POST(generateBlocksEndpoint, ep.generateBlocks)
ws.GET(initialWalletsEndpoint, ep.initialWallets)
ws.POST(setKeyValuesEndpoint, ep.setState)

return nil
}
Expand Down Expand Up @@ -66,3 +69,26 @@ func (ep *endpointsProcessor) initialWallets(c *gin.Context) {

shared.RespondWith(c, http.StatusOK, initialWallets, "", data.ReturnCodeSuccess)
}

func (ep *endpointsProcessor) setState(c *gin.Context) {
address := c.Param("address")
if address == "" {
shared.RespondWithBadRequest(c, "invalid provided address")
return
}

var keyValueMap = map[string]string{}
err := c.ShouldBindJSON(&keyValueMap)
if err != nil {
shared.RespondWithBadRequest(c, fmt.Sprintf("invalid key value map, error: %s", err.Error()))
return
}

err = ep.facade.SetState(address, keyValueMap)
if err != nil {
shared.RespondWithInternalError(c, errors.New("cannot set state"), err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing return statement after this line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return
}

shared.RespondWith(c, http.StatusOK, gin.H{}, "", data.ReturnCodeSuccess)
}
Loading
Loading