Skip to content

Commit

Permalink
Merge pull request #10 from rarimo/feat/whitelist
Browse files Browse the repository at this point in the history
Add optional 'destination' field in request. If destination in white …
  • Loading branch information
Zaptoss authored Jun 25, 2024
2 parents 74302ea + 4dea4f0 commit 89f8265
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 15 deletions.
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
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()
}

0 comments on commit 89f8265

Please sign in to comment.