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

Add optional 'destination' field in request. If destination in white … #10

Merged
merged 4 commits into from
Jun 25, 2024
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
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Relayer service that makes calls to the smart contracts. Now it is used to work
```json
{
"data": {
"tx_data": "hex_encoded_call_data"
"tx_data": "hex_encoded_call_data",
"destination": "contract_address,optional"
}
}
```
Expand Down Expand Up @@ -46,15 +47,20 @@ empty, otherwise only `Build and Publish` job will be passed.
## Config
```yaml
network:
rpc: "" # (url) RPC API endpoint
contract_address: "" # (hex) target contract address
rpc: "" # (url) RPC API endpoint | required
registration: "" # (hex) target contract address | required
private_key: "" # (hex without 0x) ECDSA secp256k1 private key for sign transactions
vault_address: "http://127.0.0.1:8200" # (url) vault address
vault_mount_path: "secret_data" # (string)
whitelist: # (list of hex addresses) specify which contracts can be passed in `destination` field in request
- "0x123...123"
- "0x123...123"
```
ENV
```
VAULT_TOKEN (will be cleared after start service)
```
There must be specified or private key, or vault address and path. If specified both, then will be used private_key from config.

## Running from docker

Expand Down
10 changes: 9 additions & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ network:
registration: ""
lightweight_state: ""
private_key: ""
vault_address: "http://127.0.0.1:8200"
vault_address: "https://127.0.0.1:8200"
vault_mount_path: "secret_data"
whitelist:
- 0x123...123
- 0x123...123
- 0x123...123
- 0x123...123
- 0x123...123
- 0x123...123
- 0x123...123

log:
level: debug
Expand Down
22 changes: 20 additions & 2 deletions internal/config/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"crypto/ecdsa"
"math/big"
"slices"
"strings"
"sync"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -32,12 +34,15 @@ type ethereum struct {
getter kv.Getter
}

type whitelist []string

type RelayerConfig struct {
RPC *ethclient.Client
RegistrationAddress common.Address
LightweightStateAddress *common.Address
ChainID *big.Int
PrivateKey *ecdsa.PrivateKey
WhiteList whitelist
nonce uint64

mut *sync.Mutex
Expand All @@ -54,8 +59,8 @@ func (e *ethereum) RelayerConfig() *RelayerConfig {
PrivateKey *ecdsa.PrivateKey `fig:"private_key"`
VaultAddress string `fig:"vault_address"`
VaultMountPath string `fig:"vault_mount_path"`
WhiteList []string `fig:"whitelist"`
}{}

err := figure.
Out(&networkConfig).
With(figure.EthereumHooks).
Expand Down Expand Up @@ -84,8 +89,17 @@ func (e *ethereum) RelayerConfig() *RelayerConfig {
panic(errors.Wrap(err, "failed to get nonce"))
}

result.mut = &sync.Mutex{}
result.WhiteList = make(whitelist, 0, len(networkConfig.WhiteList))
for _, address := range networkConfig.WhiteList {
address = strings.ToLower(address)
if result.WhiteList.IsPresent(address) {
continue
}

result.WhiteList = append(result.WhiteList, address)
}

result.mut = &sync.Mutex{}
return &result
}).(*RelayerConfig)
}
Expand Down Expand Up @@ -155,3 +169,7 @@ func extractPrivateKey(vaultAddress, vaultMountPath string) *ecdsa.PrivateKey {

return vaultRelayerConf.PrivateKey
}

func (w whitelist) IsPresent(address string) bool {
return slices.Contains(w, address)
}
19 changes: 17 additions & 2 deletions internal/service/handlers/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ func Registration(w http.ResponseWriter, r *http.Request) {
})
log.Debug("registration request")

// `RelayerConfig(r).RegistrationAddress` is default value for target contract
// if destination not specified this value will be used
// this value is required in config
registrationAddress := RelayerConfig(r).RegistrationAddress
Zaptoss marked this conversation as resolved.
Show resolved Hide resolved
if req.Data.Destination != nil {
if !RelayerConfig(r).WhiteList.IsPresent(*req.Data.Destination) {
ape.RenderErr(w, problems.BadRequest(validation.Errors{
"data/destination": fmt.Errorf("specified contract address not allowed"),
})...)
}

// destination is valid hex address because of request validation
registrationAddress = common.HexToAddress(*req.Data.Destination)
}

var txd txData
txd.dataBytes, err = hexutil.Decode(req.Data.TxData)
if err != nil {
Expand All @@ -52,7 +67,7 @@ func Registration(w http.ResponseWriter, r *http.Request) {
RelayerConfig(r).LockNonce()
defer RelayerConfig(r).UnlockNonce()

err = confGas(r, &txd, &RelayerConfig(r).RegistrationAddress)
err = confGas(r, &txd, &registrationAddress)
if err != nil {
Log(r).WithError(err).Error("failed to configure gas and gasPrice")
// `errors.Is` is not working for rpc errors, they passed as a string without additional wrapping
Expand All @@ -68,7 +83,7 @@ func Registration(w http.ResponseWriter, r *http.Request) {
return
}

tx, err := sendTx(r, &txd, &RelayerConfig(r).RegistrationAddress)
tx, err := sendTx(r, &txd, &registrationAddress)
if err != nil {
Log(r).WithError(err).Error("failed to send tx")
ape.RenderErr(w, problems.InternalError())
Expand Down
28 changes: 21 additions & 7 deletions internal/service/requests/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,39 @@ package requests
import (
"encoding/json"
"net/http"
"regexp"
"strings"

validation "github.com/go-ozzo/ozzo-validation/v4"
"gitlab.com/distributed_lab/logan/v3/errors"
)

var (
calldataRegexp = regexp.MustCompile("^0x[0-9a-fA-F]+$")
addressRegexp = regexp.MustCompile("^0x[0-9a-fA-F]{40}")
)

type RegistrationRequestData struct {
TxData string `json:"tx_data"`
TxData string `json:"tx_data"`
Destination *string `json:"destination,omitempty"`
}

type RegistrationRequest struct {
Data RegistrationRequestData `json:"data"`
}

func NewRegistrationRequest(r *http.Request) (RegistrationRequest, error) {
var request RegistrationRequest

err := json.NewDecoder(r.Body).Decode(&request)
func NewRegistrationRequest(r *http.Request) (req RegistrationRequest, err error) {
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return request, errors.Wrap(err, "failed to unmarshal")
return req, errors.Wrap(err, "failed to unmarshal")
}

if req.Data.Destination != nil {
*req.Data.Destination = strings.ToLower(*req.Data.Destination)
}

return request, nil
return req, validation.Errors{
"data/tx_data": validation.Validate(req.Data.TxData, validation.Required, validation.Match(calldataRegexp)),
"data/destination": validation.Validate(req.Data.Destination, validation.When(req.Data.Destination != nil, validation.Required, validation.Match(addressRegexp))),
}.Filter()
}
Loading