diff --git a/Tiltfile b/Tiltfile index e74a2c179..131ce8a75 100644 --- a/Tiltfile +++ b/Tiltfile @@ -193,7 +193,11 @@ WORKDIR / ) # Run data nodes & validators -k8s_yaml(["localnet/kubernetes/anvil.yaml", "localnet/kubernetes/validator-volume.yaml"]) +k8s_yaml([ + "localnet/kubernetes/anvil.yaml", + "localnet/kubernetes/rest.yaml", + "localnet/kubernetes/validator-volume.yaml" +]) # Provision validator helm_resource( @@ -370,3 +374,8 @@ if localnet_config["ollama"]["enabled"]: cmd=execute_in_pod("ollama", "ollama pull " + localnet_config["ollama"]["model"]), resource_deps=["ollama"], ) + +if localnet_config["rest"]["enabled"]: + print("REST enabled: " + str(localnet_config["rest"]["enabled"])) + deployment_create("rest", image="davarski/go-rest-api-demo") + k8s_resource("rest", labels=["data_nodes"], port_forwards=["10000"]) \ No newline at end of file diff --git a/config.yml b/config.yml index 765970617..188eef958 100644 --- a/config.yml +++ b/config.yml @@ -160,6 +160,8 @@ genesis: service_configs: - service: id: anvil + - service: + id: rest - service: id: ollama stake: @@ -181,6 +183,15 @@ genesis: rev_share: - address: pokt19a3t4yunp0dlpfjrp7qwnzwlrzd5fzs2gjaaaj rev_share_percentage: "100" + - service: + id: rest + endpoints: + - configs: [] + rpc_type: REST + url: http://relayminer1:8545 + rev_share: + - address: pokt19a3t4yunp0dlpfjrp7qwnzwlrzd5fzs2gjaaaj + rev_share_percentage: "100" - service: id: ollama endpoints: @@ -215,6 +226,10 @@ genesis: name: "ollama" compute_units_per_relay: 1 owner_address: pokt1mx0klkkrj6v3dw8gs4nzlq0cq8lsktmx35t03e + - id: rest + name: "rest" + compute_units_per_relay: 1 + owner_address: pokt1mx0klkkrj6v3dw8gs4nzlq0cq8lsktmx35t03e proof: params: proof_request_probability: "0.25" diff --git a/e2e/tests/init_test.go b/e2e/tests/init_test.go index 035c17b73..f204a4ad5 100644 --- a/e2e/tests/init_test.go +++ b/e2e/tests/init_test.go @@ -450,7 +450,13 @@ func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPa // that allows any application to send a relay in LocalNet and our E2E Tests. require.Equal(s, "app1", appName, "TODO_HACK: The LocalNet AppGateServer is self_signing and only supports app1.") - res, err := s.pocketd.RunCurlWithRetry(appGateServerUrl, serviceId, path, requestData, 5) + method := "POST" + // If requestData is empty, assume a GET request + if requestData == "" { + method = "GET" + } + + res, err := s.pocketd.RunCurlWithRetry(appGateServerUrl, serviceId, method, path, requestData, 5) require.NoError(s, err, "error sending relay request from app %q to supplier %q for service %q", appName, supplierOperatorName, serviceId) var jsonContent json.RawMessage @@ -477,6 +483,10 @@ func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPa } } +func (s *suite) TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPath(appName, supplierName, serviceId, path string) { + s.TheApplicationSendsTheSupplierASuccessfulRequestForServiceWithPathAndData(appName, supplierName, serviceId, path, "") +} + func (s *suite) AModuleEndBlockEventIsBroadcast(module, eventType string) { s.waitForNewBlockEvent(newEventTypeMatchFn(module, eventType)) } diff --git a/e2e/tests/node.go b/e2e/tests/node.go index 92fb497e8..0b6fae495 100644 --- a/e2e/tests/node.go +++ b/e2e/tests/node.go @@ -49,7 +49,7 @@ type commandResult struct { type PocketClient interface { RunCommand(args ...string) (*commandResult, error) RunCommandOnHost(rpcUrl string, args ...string) (*commandResult, error) - RunCurl(rpcUrl, service, path, data string, args ...string) (*commandResult, error) + RunCurl(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error) } // Ensure that pocketdBin struct fulfills PocketClient @@ -94,24 +94,24 @@ func (p *pocketdBin) RunCommandOnHostWithRetry(rpcUrl string, numRetries uint8, } // RunCurl runs a curl command on the local machine -func (p *pocketdBin) RunCurl(rpcUrl, service, path, data string, args ...string) (*commandResult, error) { +func (p *pocketdBin) RunCurl(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error) { if rpcUrl == "" { rpcUrl = defaultAppGateServerURL } - return p.runCurlPostCmd(rpcUrl, service, path, data, args...) + return p.runCurlCmd(rpcUrl, service, method, path, data, args...) } // RunCurlWithRetry runs a curl command on the local machine with multiple retries. // It also accounts for an ephemeral error that may occur due to DNS resolution such as "no such host". -func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, path, data string, numRetries uint8, args ...string) (*commandResult, error) { +func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, method, path, data string, numRetries uint8, args ...string) (*commandResult, error) { // No more retries left if numRetries <= 0 { - return p.RunCurl(rpcUrl, service, path, data, args...) + return p.RunCurl(rpcUrl, service, method, path, data, args...) } // Run the curl command - res, err := p.RunCurl(rpcUrl, service, path, data, args...) + res, err := p.RunCurl(rpcUrl, service, method, path, data, args...) if err != nil { - return p.RunCurlWithRetry(rpcUrl, service, path, data, numRetries-1, args...) + return p.RunCurlWithRetry(rpcUrl, service, method, path, data, numRetries-1, args...) } // TODO_HACK: This is a list of common flaky / ephemeral errors that can occur @@ -124,7 +124,7 @@ func (p *pocketdBin) RunCurlWithRetry(rpcUrl, service, path, data string, numRet if strings.Contains(res.Stdout, ephemeralError) { fmt.Println("Retrying due to ephemeral error:", res.Stdout) time.Sleep(10 * time.Millisecond) - return p.RunCurlWithRetry(rpcUrl, service, path, data, numRetries-1, args...) + return p.RunCurlWithRetry(rpcUrl, service, method, path, data, numRetries-1, args...) } } @@ -165,7 +165,7 @@ func (p *pocketdBin) runPocketCmd(args ...string) (*commandResult, error) { } // runCurlPostCmd is a helper to run a command using the local pocketd binary with the flags provided -func (p *pocketdBin) runCurlPostCmd(rpcUrl, service, path, data string, args ...string) (*commandResult, error) { +func (p *pocketdBin) runCurlCmd(rpcUrl, service, method, path, data string, args ...string) (*commandResult, error) { // Ensure that if a path is provided, it starts with a "/". // This is required for RESTful APIs that use a path to identify resources. // For JSON-RPC APIs, the resource path should be empty, so empty paths are allowed. @@ -176,9 +176,16 @@ func (p *pocketdBin) runCurlPostCmd(rpcUrl, service, path, data string, args ... base := []string{ "-v", // verbose output "-sS", // silent with error - "-X", "POST", // HTTP method + "-X", method, // HTTP method "-H", "Content-Type: application/json", // HTTP headers - "--data", data, urlStr, // POST data + urlStr, + } + + if method == "POST" { + base = append(base, "--data", data) + } else if len(data) > 0 { + fmt.Println(fmt.Sprintf("WARN: data provided but not being included in the %s request", method)) + } args = append(base, args...) commandStr := "curl " + strings.Join(args, " ") // Create a string representation of the command diff --git a/e2e/tests/relay.feature b/e2e/tests/relay.feature index e93081ef7..21609d0cc 100644 --- a/e2e/tests/relay.feature +++ b/e2e/tests/relay.feature @@ -8,14 +8,12 @@ Feature: Relay Namespace And the session for application "app1" and service "anvil" contains the supplier "supplier1" Then the application "app1" sends the supplier "supplier1" a successful request for service "anvil" with path "" and data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' - # TODO(#727): Add this test back. - # Scenario: App can send a REST relay to Supplier - # Given the user has the pocketd binary installed - # And the application "app1" is staked for service "ollama" - # And the supplier "supplier1" is staked for service "ollama" - # And the session for application "app1" and service "ollama" contains the supplier "supplier1" - # When the application "app1" sends the supplier "supplier1" a successful request for service "ollama" with path "/api/chat" and data '{"model": "qwen:0.5b", "stream": false, "messages": [{"role": "user", "content":"count from 1 to 10"}]}' - # And a "tokenomics" module "ClaimSettled" end block event is broadcast + Scenario: App can send a REST relay to Supplier + Given the user has the pocketd binary installed + And the application "app1" is staked for service "rest" + And the supplier "supplier1" is staked for service "rest" + And the session for application "app1" and service "rest" contains the supplier "supplier1" + When the application "app1" sends the supplier "supplier1" a successful request for service "rest" with path "/quote" # TODO_TEST(@Olshansk): # - Successful relay through applicat's sovereign appgate server diff --git a/localnet/kubernetes/rest.yaml b/localnet/kubernetes/rest.yaml new file mode 100644 index 000000000..1a4a9c1fc --- /dev/null +++ b/localnet/kubernetes/rest.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: rest +spec: + selector: + app: rest + ports: + - protocol: TCP + name: rpc + port: 10000 + targetPort: 10000 diff --git a/localnet/kubernetes/values-relayminer-1.yaml b/localnet/kubernetes/values-relayminer-1.yaml index 18d1c759a..b17bd5765 100644 --- a/localnet/kubernetes/values-relayminer-1.yaml +++ b/localnet/kubernetes/values-relayminer-1.yaml @@ -17,3 +17,9 @@ config: backend_url: http://ollama:11434/ publicly_exposed_endpoints: - relayminer1 + - service_id: rest + listen_url: http://0.0.0.0:8545 + service_config: + backend_url: http://rest:10000/ + publicly_exposed_endpoints: + - relayminer1 diff --git a/x/tokenomics/types/tx.pb.go b/x/tokenomics/types/tx.pb.go index 5bc7d33e4..91f06eeff 100644 --- a/x/tokenomics/types/tx.pb.go +++ b/x/tokenomics/types/tx.pb.go @@ -133,6 +133,7 @@ type MsgUpdateParam struct { // specified in the `Params` message in `proof/params.proto.` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Types that are valid to be assigned to AsType: + // // *MsgUpdateParam_AsString // *MsgUpdateParam_AsInt64 // *MsgUpdateParam_AsBytes