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

feat: fix docs and bad namings in API #2620

Merged
merged 3 commits into from
Jan 15, 2025
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
113 changes: 1 addition & 112 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ This creates a CSV file with the EVM network params in your data directory.
cargo run --bin antctl -- local run --build --clean --rewards-address <YOUR_ETHEREUM_ADDRESS>
```

The EVM Network parameters are loaded from the CSV file in your data directory automatically when the `local` option is passed to the `antctl` command.
The EVM Network parameters are loaded from the CSV file in your data directory automatically when the `local` mode is enabled.

##### 4. Verify node status

Expand Down Expand Up @@ -142,117 +142,6 @@ Now to download the files again:
cargo run --bin ant -- --local file download <addr> <dest_path>
```

### Registers

Registers are one of the network's data types. The workspace here has an example app demonstrating
their use by two users to exchange text messages in a crude chat application.

In the first terminal, using the registers example, Alice creates a register:

```
cargo run --example registers -- --local --user alice --reg-nickname myregister
```

Alice can now write a message to the register and see anything written by anyone else. For example
she might enter the text "Hello, who's there?" which is written to the register and then shown as
the "Latest value", in her terminal:

```
Register address: "50f4c9d55aa1f4fc19149a86e023cd189e509519788b4ad8625a1ce62932d1938cf4242e029cada768e7af0123a98c25973804d84ad397ca65cb89d6580d04ff07e5b196ea86f882b925be6ade06fc8d"
Register owned by: PublicKey(0cf4..08a5)
Register permissions: Permissions { anyone_can_write: true, writers: {PublicKey(0cf4..08a5)} }

Current total number of items in Register: 0
Latest value (more than one if concurrent writes were made):
--------------
--------------

Enter a blank line to receive updates, or some text to be written.
Hello, who's there?
Writing msg (offline) to Register: 'Hello, who's there?'
Syncing with SAFE in 2s...
synced!

Current total number of items in Register: 1
Latest value (more than one if concurrent writes were made):
--------------
[Alice]: Hello, who's there?
--------------

Enter a blank line to receive updates, or some text to be written.

```

For anyone else to write to the same register they need to know its xor address, so to communicate
with her friend Bob, Alice needs to find a way to send it to Bob. In her terminal, this is the
value starting "50f4..." in the output above. This value will be different each time you run the
example to create a register.

Having received the xor address, in another terminal Bob can access the same register to see the
message Alice has written, and he can write back by running this command with the address received
from Alice. (Note that the command should all be on one line):

```
cargo run --example registers -- --local --user bob --reg-address 50f4c9d55aa1f4fc19149a86e023cd189e509519788b4ad8625a1ce62932d1938cf4242e029cada768e7af0123a98c25973804d84ad397ca65cb89d6580d04ff07e5b196ea86f882b925be6ade06fc8d
```

After retrieving the register and displaying the message from Alice, Bob can reply and at any time,
Alice or Bob can send another message and see any new messages which have been written, or enter a
blank line to poll for updates.

Here's Bob writing from his terminal:

```
Latest value (more than one if concurrent writes were made):
--------------
[Alice]: Hello, who's there?
--------------

Enter a blank line to receive updates, or some text to be written.
hi Alice, this is Bob!
```

Alice will see Bob's message when she either enters a blank line or writes another message herself.

### Inspect a Register

A second example, `register_inspect` allows you to view its structure and content. To use this with
the above example you again provide the address of the register. For example:

```
cargo run --example register_inspect -- --local --reg-address 50f4c9d55aa1f4fc19149a86e023cd189e509519788b4ad8625a1ce62932d1938cf4242e029cada768e7af0123a98c25973804d84ad397ca65cb89d6580d04ff07e5b196ea86f882b925be6ade06fc8d
```

After printing a summary of the register, this example will display
the structure of the register each time you press Enter, including the following:

```
Enter a blank line to print the latest register structure (or 'Q' <Enter> to quit)

Syncing with SAFE...
synced!
======================
Root (Latest) Node(s):
[ 0] Node("4eadd9"..) Entry("[alice]: this is alice 3")
[ 3] Node("f05112"..) Entry("[bob]: this is bob 3")
======================
Register Structure:
(In general, earlier nodes are more indented)
[ 0] Node("4eadd9"..) Entry("[alice]: this is alice 3")
[ 1] Node("f5afb2"..) Entry("[alice]: this is alice 2")
[ 2] Node("7693eb"..) Entry("[alice]: hello this is alice")
[ 3] Node("f05112"..) Entry("[bob]: this is bob 3")
[ 4] Node("8c3cce"..) Entry("[bob]: this is bob 2")
[ 5] Node("c7f9fc"..) Entry("[bob]: this is bob 1")
[ 1] Node("f5afb2"..) Entry("[alice]: this is alice 2")
[ 2] Node("7693eb"..) Entry("[alice]: hello this is alice")
======================
```

Each increase in indentation shows the children of the node above.
The numbers in square brackets are just to make it easier to see
where a node occurs more than once.

### RPC

The node manager launches each node process with a remote procedure call (RPC) service. The
Expand Down
22 changes: 10 additions & 12 deletions ant-protocol/src/storage/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct GraphEntry {
pub owner: PublicKey,
pub parents: Vec<PublicKey>,
pub content: GraphContent,
pub outputs: Option<Vec<(PublicKey, GraphContent)>>,
pub outputs: Vec<(PublicKey, GraphContent)>,
/// signs the above 4 fields with the owners key
Copy link
Member

Choose a reason for hiding this comment

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

The reason for option here is that some patterns we do not have outputs, only Parent. i.e. when used as a file change history the GraphEntry can only go backwards. So the content is a direct or whatever and changes with each entry. There are no outputs in that pattern though.

Copy link
Contributor

Choose a reason for hiding this comment

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

An empty vec can be considered as a pattern. But an Option makes it more obvious, though might be a little more complex handling in code.

Copy link
Member Author

Choose a reason for hiding this comment

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

Empty vec indeed sounds like the right balance between usability and simplicity, what do you think?

pub signature: Signature,
}
Expand All @@ -33,7 +33,7 @@ impl GraphEntry {
owner: PublicKey,
parents: Vec<PublicKey>,
content: GraphContent,
outputs: Option<Vec<(PublicKey, GraphContent)>>,
outputs: Vec<(PublicKey, GraphContent)>,
signing_key: &SecretKey,
) -> Self {
let signature = signing_key.sign(Self::bytes_to_sign(&owner, &parents, &content, &outputs));
Expand All @@ -51,7 +51,7 @@ impl GraphEntry {
owner: PublicKey,
parents: Vec<PublicKey>,
content: GraphContent,
outputs: Option<Vec<(PublicKey, GraphContent)>>,
outputs: Vec<(PublicKey, GraphContent)>,
signature: Signature,
) -> Self {
Self {
Expand All @@ -68,7 +68,7 @@ impl GraphEntry {
owner: &PublicKey,
parents: &[PublicKey],
content: &[u8],
outputs: &Option<Vec<(PublicKey, GraphContent)>>,
outputs: &[(PublicKey, GraphContent)],
) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&owner.to_bytes());
Expand All @@ -83,14 +83,12 @@ impl GraphEntry {
bytes.extend_from_slice("content".as_bytes());
bytes.extend_from_slice(content);
bytes.extend_from_slice("outputs".as_bytes());
if let Some(outputs) = outputs {
bytes.extend_from_slice(
&outputs
.iter()
.flat_map(|(p, c)| [&p.to_bytes(), c.as_slice()].concat())
.collect::<Vec<_>>(),
);
}
bytes.extend_from_slice(
&outputs
.iter()
.flat_map(|(p, c)| [&p.to_bytes(), c.as_slice()].concat())
.collect::<Vec<_>>(),
);
bytes
}

Expand Down
9 changes: 9 additions & 0 deletions autonomi/nodejs/dist/graphEntry.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GraphEntryOptions, PaymentOption } from './types';
export declare class GraphEntry {
private nativeList;
private constructor();
static create(address: string): Promise<GraphEntry>;
get(): Promise<any[]>;
put(options: GraphEntryOptions, payment: PaymentOption): Promise<void>;
getCost(key: string): Promise<string>;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinkedList = void 0;
class LinkedList {
exports.GraphEntry = void 0;
class GraphEntry {
constructor(nativeList) {
this.nativeList = nativeList;
}
Expand All @@ -22,4 +22,4 @@ class LinkedList {
throw new Error('Not implemented');
}
}
exports.LinkedList = LinkedList;
exports.GraphEntry = GraphEntry;
9 changes: 0 additions & 9 deletions autonomi/nodejs/dist/linkedList.d.ts

This file was deleted.

23 changes: 12 additions & 11 deletions autonomi/src/client/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ pub enum GraphError {
}

impl Client {
/// Fetches a Transaction from the network.
pub async fn transaction_get(
/// Fetches a GraphEntry from the network.
pub async fn graph_entry_get(
&self,
address: GraphEntryAddress,
) -> Result<Vec<GraphEntry>, GraphError> {
Expand All @@ -58,12 +58,13 @@ impl Client {
Ok(transactions)
}

pub async fn transaction_put(
/// Puts a GraphEntry to the network.
pub async fn graph_entry_put(
&self,
transaction: GraphEntry,
entry: GraphEntry,
wallet: &EvmWallet,
) -> Result<(), GraphError> {
let address = transaction.address();
let address = entry.address();

// pay for the transaction
let xor_name = address.xorname();
Expand All @@ -80,7 +81,7 @@ impl Client {
Some((proof, price)) => (proof, price),
None => {
// transaction was skipped, meaning it was already paid for
error!("Transaction at address: {address:?} was already paid for");
error!("GraphEntry at address: {address:?} was already paid for");
return Err(GraphError::AlreadyExists(address));
}
};
Expand All @@ -90,7 +91,7 @@ impl Client {
let record = Record {
key: NetworkAddress::from_graph_entry_address(address).to_record_key(),
value: try_serialize_record(
&(proof, &transaction),
&(proof, &entry),
RecordKind::DataWithPayment(DataTypes::GraphEntry),
)
.map_err(|_| GraphError::Serialization)?
Expand Down Expand Up @@ -136,10 +137,10 @@ impl Client {
Ok(())
}

/// Get the cost to create a transaction
pub async fn transaction_cost(&self, key: SecretKey) -> Result<AttoTokens, GraphError> {
/// Get the cost to create a GraphEntry
pub async fn graph_entry_cost(&self, key: SecretKey) -> Result<AttoTokens, GraphError> {
let pk = key.public_key();
trace!("Getting cost for transaction of {pk:?}");
trace!("Getting cost for GraphEntry of {pk:?}");

let address = GraphEntryAddress::from_owner(pk);
let xor = *address.xorname();
Expand All @@ -151,7 +152,7 @@ impl Client {
.map(|quote| quote.price())
.sum::<Amount>(),
);
debug!("Calculated the cost to create transaction of {pk:?} is {total_cost}");
debug!("Calculated the cost to create GraphEntry of {pk:?} is {total_cost}");
Ok(total_cost)
}
}
12 changes: 6 additions & 6 deletions autonomi/tests/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,28 @@ async fn transaction_put() -> Result<()> {

let key = bls::SecretKey::random();
let content = [0u8; 32];
let transaction = GraphEntry::new(key.public_key(), vec![], content, vec![].into(), &key);
let transaction = GraphEntry::new(key.public_key(), vec![], content, vec![], &key);

// estimate the cost of the transaction
let cost = client.transaction_cost(key.clone()).await?;
let cost = client.graph_entry_cost(key.clone()).await?;
println!("transaction cost: {cost}");

// put the transaction
client.transaction_put(transaction.clone(), &wallet).await?;
client.graph_entry_put(transaction.clone(), &wallet).await?;
println!("transaction put 1");

// wait for the transaction to be replicated
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;

// check that the transaction is stored
let txs = client.transaction_get(transaction.address()).await?;
let txs = client.graph_entry_get(transaction.address()).await?;
assert_eq!(txs, vec![transaction.clone()]);
println!("transaction got 1");

// try put another transaction with the same address
let content2 = [1u8; 32];
let transaction2 = GraphEntry::new(key.public_key(), vec![], content2, vec![].into(), &key);
let res = client.transaction_put(transaction2.clone(), &wallet).await;
let transaction2 = GraphEntry::new(key.public_key(), vec![], content2, vec![], &key);
let res = client.graph_entry_put(transaction2.clone(), &wallet).await;

assert!(matches!(
res,
Expand Down
Loading
Loading