Giter Club home page Giter Club logo

p2panda's Introduction

p2panda

All the things a panda needs


This library provides all tools required to write a client, node or even your own protocol implementation for the p2panda network. It is shipped both as a Rust crate p2panda-rs with WebAssembly bindings and a NPM package p2panda-js with TypeScript definitions running in NodeJS or any modern web browser.

The core p2panda specification is fully functional but still under review so please be prepared for breaking API changes until we reach v1.0. Currently no p2panda implementation has recieved a security audit.

Features

  • Generate Ed25519 key pairs.
  • Create and encode Bamboo entries.
  • Publish schemas and validate data.
  • Create, update and delete data collaboratively.
  • Encrypt data with OpenMLS.
  • Materialise documents from data changes.
  • Prepare data for node servers.

Usage

import { KeyPair } from "p2panda-js";
const keyPair = new KeyPair();
console.log(keyPair.publicKey());
use p2panda_rs::identity::KeyPair;
let key_pair = KeyPair::new();
println!("{}", key_pair.public_key());

See the demo application and its source code. More examples can be found in the p2panda-rs and p2panda-js directories.

Installation

If you are using p2panda in web browsers or NodeJS applications run:

$ npm i p2panda-js

For Rust environments run:

$ cargo add p2panda-rs

Documentation

Visit the corresponding folders for development instructions and documentation:

Benchmarks

Performance benchmarks can be found in benches. You can run them using cargo-criterion:

$ cargo install cargo-criterion
$ cargo criterion
# An HTML report with plots is generated automatically
$ open target/criterion/reports/index.html

These benchmarks can be used to compare the performance across branches by running them first in a base branch and then in the comparison branch. The HTML-reports will include a comparison of the two results.

License

GNU Affero General Public License v3.0 AGPL-3.0-or-later

Supported by



This project has received funding from the European Union’s Horizon 2020 research and innovation programme within the framework of the NGI-POINTER Project funded under grant agreement No 871528 and NGI-ASSURE No 957073

p2panda's People

Contributors

adzialocha avatar cafca avatar nichoth avatar pietgeursen avatar sandreae avatar sophiiistika avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

p2panda's Issues

Make `previous_seq_num` just `seq_num`

Passing over the "previous" sequence number to the Entry::new constructor is confusing. You assume you just give it the values it will hold but instead it assumes you give it something from "before", thats strange and should be changed 🐈

Refactor `String` representation of atomic values

We currently implement our atomic structs like EntrySigned, MessageEncoded, Hash and Author around the String type: They are all simple wrappers around simple strings, like here: https://github.com/p2panda/p2panda/blob/main/p2panda-rs/src/atomic/author.rs#L29

This is nice because we can easily work with databases, the JavaScript world (via WebAssembly) or network data as all p2panda data types are represented as hex-encoded strings in these cases.

But also this is a little confusing as these "usecases" are not clearly visible when reading the p2panda-rs code and API. Maybe there is a way to separate these concerns a little bit better aka have clear structs with well defined methods and still support nice hex string serialization and deserialization for databases (via sqlx) and JSON RPC requests (via serde)?

We could for example make use of the Encode and Decode traits to work with sqlx:
https://docs.rs/sqlx/0.5.2/sqlx/trait.Encode.html

Consider `Entry` and `EntrySigned` conversion traits

Is this the best structure for the entry signing logic?

problems with current situation:

  • conceptually confusing
  • the conversion process is lossy and requires a tuplet, which go against normal rust conversion trait convention (maybe..?)

alternative ideas:

  • a sign() method which lives in the Entry struct and accepts a key_pair and returns an ÈntrySigned
  • a sign() function which lives on it's own in sign.rs
  • something else...

Compress transported data between servers and clients

Implement compression of the encoded entry and message data before it gets sent to the node.

We could add it as an extension of the RPC protocol (like, when a compression header flag exists the client / server will compress / decompress accordingly).

Restructure files in `atomic` module

Ideas for restructuring files and modules in p2panda-rs:

  • error
  • hash Hash
  • identity
    • keypair KeyPair
    • author Author
  • message
    • message Message
    • message_encoded MessageEncoded
    • encode? encode_message
  • entry
    • seq_num SeqNum
    • entry Entry
    • entry_signed EntrySigned
    • log_id LogId
    • encode? sign_and_encode
    • decode? decode_entry
    • verify? verify_entry
  • schema
  • wasm

Create Typescript declarations with build

  • Create a single index.d.ts in the build folder with types for the exported module
  • No import paths in the declaration that don't resolve independent of the p2panda repository
  • Unset env var in npm run test that disables type checks

Logic for backlink_seq_num and skiplink_seq_num in SeqNum

It looks to me like there is something funny going on in the logic for calculating skiplink_seq_num and backlink_seq_num in SeqNum. I added a failing test to the experimental branch to demonstrate. As I understand it if the entries seq_num is 2 then both backlink_seq_num and backlink_seq_num should both equal 1.

#[test]
fn skiplink_backlink_equal() {
assert_eq!(
SeqNum::new(2).unwrap().backlink_seq_num().unwrap(),
SeqNum::new(2).unwrap().skiplink_seq_num().unwrap(),
);
}

Reference lipmaa tests can be seen here: https://github.com/pietgeursen/lipmaa-link/blob/master/src/lib.rs

Seems like we're being inconsistent with how we're offsetting the return values with FIRST_SEQ_NUM.

Maybe I'm misunderstanding how these methods are to be used though...

Move `Message` out of `Entry` struct

The Entry struct contains a Message: https://github.com/p2panda/p2panda/blob/main/p2panda-rs/src/atomic/entry.rs#L67 but later on its not really used anywhere.

The only place where it is used is when signing and encoding the entry: https://github.com/p2panda/p2panda/blob/main/p2panda-rs/src/atomic/entry_signed.rs#L124

We could remove the message field from the Entry struct and make it an argument one needs to pass over when signing the entry in EntrySigned? It would look like this then:

impl TryFrom<(&Entry, &Message, &KeyPair)> for EntrySigned {
    type Error = EntrySignedError;

    fn try_from((entry, message, key_pair): (&Entry, &Message, &KeyPair)) -> Result<Self, Self::Error> {
      // ...

Server returns `message needs to match payload hash of encoded entry`

This started to occur when adding the date field (eg. increasing the message payload size) into beep-boop (see: p2panda/zoo-adventures@26d79cc). For some entries the aquadoggo rejects the panda_publishEntry request, when repeating the request it succeeds sometimes.

The error comes from p2panda-rs: https://github.com/p2panda/p2panda/blob/main/p2panda-rs/src/atomic/entry.rs#L164 when converting the incoming encodedEntry here: https://github.com/p2panda/aquadoggo/blob/main/aquadoggo/src/rpc/methods/publish_entry.rs#L32

Request

{
  "jsonrpc": "2.0",
  "id": "2",
  "method": "panda_publishEntry",
  "params": {
    "entryEncoded": "00a999eebab4cc8e9fcc529a24ae3523eaa34c8ccb1eaba673ff4b1cfcfa14fd210101e5004093de7a6e8430a57cc0685b46ab0ecb54cd898c9f08c9099c41f65912da6a955024268789ceb25308f030e21b026f5e29734186819c48aa0ba53f6e2b99a9a75a72e355534060eadfa6bf4d70a5569a22f805201dd2d29503091cb3a28b37ee9da9aa8b78c7472f04ace4d86ef516c796435c7d1d4582c3e0f59dd1829d1c860e",
    "messageEncoded": "a466616374696f6e6663726561746566736368656d6178843030343063663934663664363035363537653930633534336230633931393037306364616166373230396335653165613538616362386633353638666132313134323638646339616333626166653132616632373764323836666365376463353962376330633334383937336334653964616362653739343835653536616332613730326776657273696f6e01666669656c6473a2676d657373616765a1645465787464746573746464617465a164546578747818323032312d30342d31365430383a30323a34372e3138355a"
  }
}

Response

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32602,
    "message": "Invalid params: message needs to match payload hash of encoded entry."
  },
  "id": "2"
}

More context

Rehashing the messageEncoded payload using p2panda-rs results in this hash: 0040afd364be6a38baa45a9db7096fe72aac00aa90753929e9458c752d263bc987bcaa83453569b956c37a9e985e5e3c52ab3d901334524356b1e22348fc95f46d1c. The following code was used:

fn test_hash() {
    let message_encoded = MessageEncoded::new("a466616374696f6e6663726561746566736368656d6178843030343063663934663664363035363537653930633534336230633931393037306364616166373230396335653165613538616362386633353638666132313134323638646339616333626166653132616632373764323836666365376463353962376330633334383937336334653964616362653739343835653536616332613730326776657273696f6e01666669656c6473a2676d657373616765a1645465787464746573746464617465a164546578747818323032312d30342d31365430383a30323a34372e3138355a").unwrap();
    println!("{:?}", message_encoded.hash());
}

Looking at the entryEncoded payload we can see the following hash: 004093de7a6e8430a57cc0685b46ab0ecb54cd898c9f08c9099c41f65912da6a955024268789ceb25308f030e21b026f5e29734186819c48aa0ba53f6e2b99a9a75 which is clearly different from the above result.

Test new p2panda-js api in browser environment

There may be problems with bundling that we are not catching because our test suite only runs in a node environment.


May need this code in loadWasm():

if (this.p2panda.default != null) {
  log('fallback loader from `p2panda.default`');
  this.p2panda = await p2panda.default;
} else {
  log('loaded wasm lib');
}

Correct format of typed message fields

Finalize the specification of typed fields in messages:

Before

...
fields: {
   message: {
     Text: "Hello, Panda!"
   }
   ...
}

After

...
fields: {
   message: {
     value: "Hello, Panda!",
     type: "text"
   }
   ...
}

The new format also has the advantage to add future parameters to fields.

Define author encoding

How do we present an author's public key to users?

  • Hexadecimal 1a8a62c5f64eed987326513ea15a6ea2682c256ac57a418c1c92d96787c8b36e (64 chars)
  • Base64 (ala SSB) GopixfZO7ZhzJlE+oVpuomgsJWrFekGMHJLZZ4fIs24= (43 chars)
  • Base64url GsKKYsOFw7ZOw63CmHMmUT7CoVpuwqJoLCVqw4V6QcKMHMKSw5lnwofDiMKzbg== https://tools.ietf.org/html/rfc4648#section-5

Improve `MessageFields` wasm export

There must be a better way to write the MessageFields wasm export in https://github.com/p2panda/p2panda/blob/main/p2panda-rs/src/wasm.rs#L35-L68:

  • All other wasm methods follow a functional style except of this one, is it possible to do the same?
  • Using JsValue as an argument type feels strange, as we introduce the "wild type west" of the JavaScript world into Rust. Maybe we need to refactor the add method to be more type strict / being less generic? For example by introducing more methods like add_text_field, add_float_field, add_integer_field etc.?

Related: #52

Separate wasm exports from actual methods

The library should not only be used in wasm contexts, we should write the library methods as if we only deal with Rust and have separate wasm exports next to them. We can also explorer how to make sure the methods can also be bind to C, C++, Go etc. but thats something for later.

Implement JSON-RPC client

Requirement for implementing rpc methods.

  • Find a place for the client to live in
  • Write a Readme with some basic usage examples

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.