Skip to main content
Version: Next

open/4

Description

open/4 is a predicate which opens a stream to a source or sink.

Signature

open(+SourceSink, +Mode, -Stream, +Options)

where:

  • SourceSink is an atom representing the source or sink of the stream in the virtual file system.
  • Mode is an atom representing the mode of the stream to be opened (for example "read", "write", "append", "read_write").
  • Stream is the stream to be opened.
  • Options is a list of stream options.

open/4 gives True when SourceSink can be opened in Mode with the given Options.

Virtual File System \(VFS\)

The logical module interprets on-chain Prolog programs, relying on a Virtual Machine that isolates execution from the external environment. Consequently, the open/4 predicate doesn't access the physical file system as one might expect. Instead, it operates with a Virtual File System (VFS), a conceptual layer that abstracts the file system. This abstraction offers a unified view across various storage systems, adhering to the constraints imposed by blockchain technology.

This VFS extends the file concept to module-provided resources and devices exposed as paths, for example:

  • immutable snapshots under /v1/sys/*
  • transactional devices under /v1/dev/*

Examples

Open a resource for reading

This scenario demonstrates how to build a VFS path and open it in read mode.

Here are the steps of the scenario:

  • Given the program:
resource_path(Path) :-
atomic_list_concat(['/v1', 'sys', 'header', 'height'], '/', Path).
  • Given the query:
resource_path(Path),
open(Path, read, _, []).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 6864
answer:
has_more: false
variables: ["Path"]
results:
- substitutions:
- variable: Path
expression: "'/v1/sys/header/height'"

Open an existing resource and read its Prolog term

This scenario demonstrates how to open a text resource, read one term, and close the stream.

Here are the steps of the scenario:

  • Given the program:
read_height(Path, Height) :-
open(Path, read, Stream, [type(text)]),
read_term(Stream, Height, []),
close(Stream).
  • Given the query:
read_height('/v1/sys/header/height', Height).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 4100
answer:
has_more: false
variables: ["Height"]
results:
- substitutions:
- variable: Height
expression: "42"

Open a wasm query endpoint in read_write mode

This scenario demonstrates how to write request bytes, then read response bytes from a transactional endpoint.

Here are the steps of the scenario:

  • Given the CosmWasm smart contract "axone15ekvz3qdter33mdnk98v8whv5qdr53yusksnfgc08xd26fpdn3tsrhsdrk" and the behavior:
message: |
{}
response: |
{"ok":true}
  • Given the program:
read_all_bytes(Stream, Bytes) :-
get_byte(Stream, Byte),
( Byte =:= -1 ->
Bytes = []
; Bytes = [Byte | Rest],
read_all_bytes(Stream, Rest)
).

wasm_roundtrip(Address, ResponseBytes) :-
atom_concat('/v1/dev/wasm/', Address, Prefix),
atom_concat(Prefix, '/query', Path),
open(Path, read_write, Stream, [type(binary)]),
put_byte(Stream, 123),
put_byte(Stream, 125),
read_all_bytes(Stream, ResponseBytes),
close(Stream).
  • Given the query:
wasm_roundtrip('axone15ekvz3qdter33mdnk98v8whv5qdr53yusksnfgc08xd26fpdn3tsrhsdrk', ResponseBytes).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 5641
answer:
has_more: false
variables: ["ResponseBytes"]
results:
- substitutions:
- variable: ResponseBytes
expression: "[123,34,111,107,34,58,116,114,117,101,125]"

Try to open a non-existing resource

This scenario demonstrates the system's response to trying to open a non-existing resource.

Here are the steps of the scenario:

  • Given the query:
open('foo:bar', read, Stream, []).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 3935
answer:
has_more: false
variables: ["Stream"]
results:
- error: "error(existence_error(source_sink,foo:bar),open/4)"

Try to open a read-only resource for writing

This scenario demonstrates the system's response to opening a snapshot path in write mode.

Here are the steps of the scenario:

  • Given the query:
open('/v1/sys/header/height', write, Stream, []).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 3950
answer:
has_more: false
variables: ["Stream"]
results:
- error: "error(permission_error(open,source_sink,/v1/sys/header/height),open/4)"

Try to open a read-only resource for appending

This scenario demonstrates the system's response to opening a snapshot path in append mode.

Here are the steps of the scenario:

  • Given the query:
open('/v1/sys/header/height', append, Stream, []).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 3951
answer:
has_more: false
variables: ["Stream"]
results:
- error: "error(permission_error(open,source_sink,/v1/sys/header/height),open/4)"

Pass incorrect options to open/4

This scenario demonstrates the system's response to opening a resource with incorrect options.

Here are the steps of the scenario:

  • Given the query:
open('/v1/sys/header/height', read, Stream, [non_existing_option]).
  • When the query is run
  • Then the answer we get is:
height: 42
gas_used: 3971
answer:
has_more: false
variables: ["Stream"]
results:
- error: "error(domain_error(stream_option,non_existing_option),open/4)"