Code Monkey home page Code Monkey logo

five-bells-condition's Introduction

Interledger.js Monorepo

status codecov

Packages

Payments

Name Version Description
@interledger/pay NPM Package Send payments over Interledger using STREAM
@interledger/stream-receiver NPM Package Simple & composable stateless STREAM receiver

Utilities

Name Version Description
ilp-logger NPM Package Debug logging utility for Interledger modules
ilp-packet NPM Package Serialization/deserialization utility for ILP packets
ilp-plugin NPM Package Connect to a local, open BTP server
ilp-plugin-btp NPM Package One plugin to rule them all
ilp-protocol-ccp NPM Package Serialization/deserialization for the CCP routing protocol
ilp-protocol-ildcp NPM Package Fetch asset and account details from a parent
ilp-protocol-stream NPM Package Reliably send streams of money and data over ILP
oer-utils NPM Package Tools for OER parsing and serialization

Installation

The monorepo is set up to use lerna and pnpm workspaces. To get started run the following:

  1. pnpm install - pnpm will install the dependencies and do the necessary linking (no need to run lerna bootstrap).
  2. pnpm build
  3. pnpm test - This will run the tests in all the packages.

Running script commands

Script commands such as test and lint can be run from the root of the project by running:

# Run tests for all packages
pnpm test

# Run tests for a specific module a package
pnpm test --scope=<package-name>

Or in the package directory:

pnpm test

If you are interested in contributing, please read the contributing guidelines.

For Maintainers

Versioning

Independent versioning is used for this project and releases can only be made from master. You will need to set the GH_TOKEN env variable to your personal GitHub access token. Please make sure that you are up to date with master and that the tests and linting pass. Then use the following to create a release:

# On master
GH_TOKEN=<github-token> lerna version --conventional-commits --create-release github

and follow the command prompts. This will commit the package version changes and create the necessary tags - all of which will be pushed to master. It will also create changelogs and official GitHub releases.

If you want to release an alpha then run

# On master
GH_TOKEN=<github-token> lerna version --conventional-commits --conventional-prerelease --create-release github

This will append -alpha.<alpha-version> to the release name. The alpha release can be graduated (1.0.1-alpha.1 => 1.0.1) by running:

# On master
GH_TOKEN=<github-token> lerna version --conventional-commits --conventional-graduate --create-release github

Adding new packages

All source code is expected to be TypeScript and is placed in the src folder. Tests are put in the test folder. The NPM package will not contain any TypeScript files (*.ts) but will have typings and source maps. A typical project should have the following structure:

|-- src
|-- test
|-- package.json
|-- tsconfig.build.json

The tsconfig.build.json file should have the following

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "composite": true,
    "baseUrl": ".",
    "rootDir": "src",
    "outDir": "dist",
    "tsBuildInfoFile": "./dist/tsconfig.build.tsbuildinfo"
  },
  "include": [
    "src"
  ]
}

The package.json file should specify the following

{
  "name": "<package-name>",
  "license": "Apache-2.0",
  "publishConfig": {
    "access": "public"
  }
}

In the scripts section of the package.json, be sure to have build, cover (which runs tests with coverage) and codecov. These will be called from the CI pipeline. Please use the following as a guideline:

"scripts": {
  "build": "tsc -p tsconfig.build.json",
  "cover": "...",
  "codecov": "curl -s https://codecov.io/bash | bash -s - -s coverage -F <flagname>"
}

The cover script should run the tests with code coverage and output the coverage results in a format that can be uploaded to codecov. The flagname will be used by codecov to track coverage per package. Please make sure it matches the regex ^[a-z0-9_]{1,45}$.

Importing legacy modules

This process preserves the commit history of the legacy modules.

git clone [email protected]:adrianhopebailie/interledgerjs.git
git clone [email protected]:interledgerjs/legacy-module.git
cd legacy-module
git pull
cd ../interledgerjs
lerna import ../legacy-module --dest=packages --preserve-commit --flatten

You then need to replace the tsconfig.json file with the tsconfig.build.json and update the package.json as described above.

Dependencies

We keep devDependencies that are shared across all packages in the root package.json file. Dependencies can be added to individual packages using Lerna

lerna add <package to install> --scope=<package-name>

# Add dev dependency
lerna add <package to install> --scope=<package-name> --dev

Running script commands

Script commands such as test and lint can be run from the root of the project by running

# All tests in all packages
lerna run test

#Scoping to a package
lerna run test --scope=<package-name>

five-bells-condition's People

Contributors

adrianhopebailie avatar greenkeeperio-bot avatar jernejpregelj avatar jtremback avatar justmoon avatar michielbdejong avatar sbellem avatar sentientwaffle avatar stevenroose avatar vhpoet 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

Watchers

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

five-bells-condition's Issues

Is rejecting URI's with base64 padding required?

RFC 4648 states:

The pad character "=" is typically percent-encoded when used in an
   URI [9], but if the data length is known implicitly, this can be
   avoided by skipping the padding; see section 3.2.

This does not imply that padding is SHOULD be left out (as currently five bells interprets it). However, it might be encoded using the percent encoded character (%3D), f.e. when it has been used in a URL, or it COULD be left out.

Add an elliptic curve condition type

Based on our criteria:

  • Should use ECSchnorr (simple, threshold support, etc. - more info)
  • Standardized
  • No magic inputs
  • Fast
  • Approximately 128 bits of security

The curve that matches most closely seems to be Ed25519, which is widely used and in the process of being standardized.

The next closest option is the Schnorr version of secp256k1. Implementation here. No standardization yet and little usage outside of Bitcoin. Has a slight advantage over Ed25519 due to the lack of a cofactor. For more background on the disadvantages of having a cofactor, see this paper by Mike Hamburg, section 1.1. Note that this is more a nitpick than a dealbreaker.

Thanks to @tarcieri for important input on this.

Spec: max message length

Similar to how there is a maxFulfillmentLength, I think there should be a maxMessageLength. This is unusual for a signature scheme but we want to prevent the case where someone can set up conditions on multiple systems knowing that a message + fulfillment will be valid on one but not the other.

When a condition's bitmask contains multiple structural types, which structural type is it?

The bitmask works by setting bits based on the type of the structural condition and the types of subconditions.

However, what happens when a subcondition is also a structural condition of a different type?

For example, if 0x04 is the threshold SHA256 condition type, 0x40 is the threshold SHA512 condition type and 0x01 is the SHA256 hashlock type, what type of condition is 0x45? Is it a SHA512 threshold condition with SHA256 threshold and hashlock types in it? Or is it a SHA256 threshold condition with SHA256 hashlock and SHA512 threshold subconditions in it.

Proposed solution:

Give each condition type a unique ID which also matches the position of its bit in the bitmask. So condition 1 is 0x01, 2 is 0x02, 3 is 0x04 and so on. Use the version field to denote the condition type. Only meta types provide a bitmask. The condition type field can double as a version field since the following data depends on the condition type and implementations will reject types they do not recognize.

Examples:

// Types:
// 1 ... 0x01 ... SHA256 hashlock
// 2 ... 0x02 ... RSA-SHA256
// 3 ... 0x04 ... SHA256 threshold
// 7 ... 0x40 ... SHA512 threshold

Simple SHA256 hashlock
cc:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1
// 1 ... version 1 = SHA256 hashlock

SHA256 threshold containing SHA256 hashlock
cc:3:5:UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw:50
// 3 ... version 3 = SHA256 threshold
// 5 ... bitmask 0x05 =
//    0x01 SHA256 hashlock
//    0x04 SHA256 threshold

SHA512 threshold containing SHA256 threshold and hashlock
cc:7:5:X7FnnghnQFm3LicdiQLBGhJ7tTAbBV3Hf6A5Mq2lalY:50
// 7 ... version 7 = SHA512 threshold
// 5 ... bitmask 0x05 =
//    0x01 SHA256 hashlock
//    0x04 SHA256 threshold

SHA256 threshold containing SHA512 threshold and SHA256 hashlock
cc:3:41:yuumEiY8oD40Uo5_FCkzYj_ELArGV5C6CeGk43qtFcE:50
// 3 ... version 3 = SHA256 threshold
// 41 ... bitmask 0x41 =
//    0x01 SHA256 hashlock
//    0x40 SHA512 threshold

Spec: Break out prefix type

If we make prefixes a separate type, we can omit them for unfulfilled subconditions:

Current format:

04 01 VARARRAY[
  01 02 FFFF FULFILLMENT 
  01 02 ABCD CONDITION
]

Proposed format:

04 01 VARARRAY[
  01 10 02 FFFF FULFILLMENT
  01 CONDITION
]

Simple condition validation API

Right now (on the binary-merkle-v2 branch) the condition.validateFulfillment method returns an object that looks like:

{
  valid: true,
  condition: 'cc:1:8:TqLE1i0L68lpsEAfeviEoD1U1K_1Z_XNG0VX1TcHOwg:101',
  error: null
}

If I already have the condition I would prefer to have an API that looks something like:

const condition = require('five-bells-condition')
const conditionString = 'cc:1:8:TqLE1i0L68lpsEAfeviEoD1U1K_1Z_XNG0VX1TcHOwg:101'
const fulfillmentString = 'cf:1:8:ICiMf6rgj1JLUH3ZPJ3LnLJWrAHEjYhtH44nQ7MKrWdlICv2WUHv2IpHQmoMg8kJew8hYZo2OEL0UGLi_CpE9T5oACAr9llB79iKR0JqDIPJCXsPIWGaNjhC9FBi4vwqRPU-aECS24epmvbAijxWXCaqL_wkMnD67K0ekTViFC1ygRknxzHpHbZyU4IB7xAGD_Xi5L4ebtjreH__ltGwFfgM9usH'

console.log(condition.isValidFulfillment(conditionString, fulfillmentString)) // prints true or false
// or
condition.validateFulfillment(conditionString, fulfillmentString) // throws an error if it is invalid

For getting the condition from the fulfillment, I might suggest a more explicit static method like:

condition.getConditionFromFulfillment(fulfillmentString)

On the naming front, I would personally expect a validate function to throw an error if something is invalid, an isValid function to return true/false, and a parse method or something like that to return some details like what's returned right now from condition.validateFulfillment. I know that some validator libraries return objects with errors in one of the fields from their validator methods, but this would be my preference.

Puzzled by Condition validation

The code for validating a condition is :

  validate () {
    // Get info for type ID, throws on error
    TypeRegistry.findByTypeId(this.getTypeId())

    // Bitmask can have at most 32 bits with current implementation
    if (this.getSubtypes() > Condition.MAX_SAFE_SUBTYPES) {
      throw new Error('Bitmask too large to be safely represented')
    }

    // Assert all requested features are supported by this implementation
    if (this.getSubtypes() & ~Condition.SUPPORTED_SUBTYPES) {
      throw new Error('Condition requested unsupported feature suites')
    }

    // Assert the requested fulfillment size is supported by this implementation
    if (this.getCost() > Condition.MAX_COST) {
      throw new Error('Condition requested too large of a max fulfillment size')
    }

    return true
  }

Given that subtypes is a set of strings, e.g.: Set {'preimage-sha-256', 'ed25519-sha-256'} what is the purpose of the checks > and & ~?

Throw a more descriptive error if no message is provided when validating an ed25519 condition/fulfillment

Found by @inacali in #51

If you run the following and leave out the message from the second to last line you'll see a ValidationError: Invalid ed25519 signature error. It should instead tell you that you need to pass in a message.

const cc = require('five-bells-condition')
const ed25519 = require('ed25519')

const seed =  Buffer.from('833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42', 'hex')
const keypair = ed25519.MakeKeypair(seed)
const edff = new cc.Ed25519()
edff.setPublicKey(keypair.publicKey)
const condition = edff.getConditionUri()
const message = new Buffer('Hello World! Conditions are here!')
edff.sign(message, seed)
const fulfillment = edff.serializeUri()
const valid = cc.validateFulfillment(fulfillment, condition, message)
console.log('valid', valid)

Reason for identical Fulfillment.serializeBase64Url and .serializeUri?

The two functions are identical:

/**
 * Base class for fulfillment types.
 */
class Fulfillment {

  serializeUri () {
    return base64url.encode(this.serializeBinary())
  }

  serializeBase64Url () {
    return base64url.encode(this.serializeBinary())
  }

Is one preferred over the other moving forward?

Decide on nomenclature

We've gotten feedback from various people that the current nomenclature is not very clear. We should consider nomenclature carefully and change it if necessary. The window for doing that is closing quickly.

Specify padding for base64URL encoded values

We may want to specify the padding for base64URL encoding in https://interledger.org/five-bells-condition/spec.html.

From rfc4648 :

In some circumstances, the use of padding ("=") in base-encoded data
   is not required or used.  In the general case, when assumptions about
   the size of transported data cannot be made, padding is required to
   yield correct decoded data.

   Implementations MUST include appropriate pad characters at the end of
   encoded data unless the specification referring to this document
   explicitly states otherwise.

It looks like some implementation (like the one in this directory) drops padding. Implementations in other languages may not (for instance in Java and Haskell), creating inconsistencies. Comparing conditions becomes more complex in case of optional padding.

Readme has confusing example usage of validateFulfillment

The following snippet found in a few places in the readme makes it look like validateFulfillment returns a boolean to indicate validation whereas it actually throws an error for invalid fulfillments.

const validationResult = cc.validateFulfillment(fulfillment, condition)
// validationResult === true

M-of-N conditions?

Not sure if this is the right place, but wondering how to use this repo code to create an m-of-n condition and identify which exact m (out of the all n) conditions were fulfilled?

Any example code, or pointers to the code would be very helpful.

Diffeculty in fulfilling a ed25519 signature

Hi guys I am playing around with interledger at the moment, and am trying to better understand crypto conditions by building a simple client using ilp-core and five bells ledger ilp plugin, so far I've got the example to work with the pre-image 256, but having difficulty in getting an ed25519 signature to fulfill, the transaction is failing due to condition not being fulfilled, Am i right in thinking the fulfillment serialized uri is what is required to fulfill the condition of an ed25519 signature or is it something else? Whilst fulfilling the condition on the reciever client I am getting an UnmetConditionError.

Here are the steps I've taken so far

  1. First I created the ed25519 condition like in the readme section of FB conditons, by creating a ed25519() and setting a public key.
  2. The sender then sends crypto condition along with the other transfer details to receiver.
  3. The receiver receives the transaction, and signs a message with his privateKey to fulfill the condition.
  4. The receiver calls the serializeUri() method after signing the message with his private key to get the fulfillmentUri.
  5. The receiver then then tries to fulfill the condition by using this fulfillCondition method, with fulfillment being the fulfillmentUri.

receiver.on('receive', function(transfer){ console.log("Here is the transfer", transfer ) receiver.fulfillCondition(transfer.id, fulfillment) .then(() => { console.log('Successfully submitted fulfillment' + fulfillment) return 'sent' }) .catch((err) => { console.log('Error submitting fulfillment', err) }) })

  1. instead of fulfilling like in the preimage example. I get the following error. ExternalError: Remote error: status=422 body={"id":"UnmetConditionError","message":"Invalid fulfillment: ValidationError: Invalid ed25519 signature"}
  2. Even though when validate the condition and fulfillment using const result = cc.validateFulfillment(fulfillment, condition, message) I get true.

Am i missing a step in fulfilling a signature? or is something else incorrect. Sorry in advance if this is wrong forum for these type of questions?

Return validation result instead of throwing errors

Throwing errors may be useful if one is targeting to build end-user applications, but for developing middleware it would be very cumbersome to juggle the flow around the exceptions.

It would be great if the error throwing could be replaced with normal function returns, and let the caller decide to throw or not. Debugging becomes easy for developers and goes easy on CPU too (not to mention the readability of examples improve a lot #59 )

Its impossible to write below kind of simple code, very useful for pedagogic reasons, when errors are thrown liberally.

console.log("validation Result for AAA: ", cc.validateFulfillment(fulfillment1, condition));
console.log("validation Result for BBB: ", cc.validateFulfillment(fulfillment2, condition));

Not to mention, trying to validate an array of fulfillments in for loop or Array map or piping with lodash primitives!!

 results = fulfillmentsArray.map(f => cc.validateFulfillment(f, condition);
 _.some(results,  result => { return result == true });

The returned result value could be object (instead of plain boolean value), if required.

   _.some(results,  result => { return result.fulfilled == true });

Define recommended MIME types

It's good practice to recommend a specific MIME type that people should use, so we should put some thought into it and come up with recommended MIME types for text and binary crypto-conditions and fulfillments.

"Implicit" ASN fields

I did the best part of an implementation of Crypto-Conditions in Haskell (link) (I work for BigchainDB), and when I came to test my code against the provided test suite (very useful btw), there were a few things that were not as I expected, so I will outline them here as feedback:

  1. ASN

Throughout the spec, the ASN encoding is expressed in terms of ASN field types, however, the encoded binary does not contain the type headers, instead the field headers are "implicit" and manually numbered.

I am using the hs-asn1 libraries which have a data type to describe ASN1 encodable data, rather than using models, but the calls to implicit(0) are apparent in the Javascript code.

Below is an example of how that looks, encoding a payload that contains "hello", then a Sequence of an integer and then "world".

I expected:

[ OctetString "hello"
, Start Sequence
, IntVal 1
, OctetString "world"
, End Sequence
]

"\EOT\ENQhello0\n\STX\SOH\SOH\EOT\ENQworld"

But instead it's:

[ Other Context 0 "hello"
, Start (Container Context 1)
, Other Context 0 "\SOH"
, Other Context 1 "world"
, End (Container Context 1)
]

"\128\ENQhello\161\n\128\SOH\SOH\129\ENQworld"

So you can see that the field headers are different but the data is the same. Although the spec specifies SEQUENCE OF, usually I had to enter Start (Container Context n), where n is the index of the sequence in the parent data structure.

The exception to this is in the fingerprints, where the payload that gets hashed is in fact wrapped in a Sequence, at least according to my code. Looking at the Javascript code, this.seq() is always called, it could be that a call to implicit(n) is what ends up setting the header of the container, I don't know.

I'm not sure if the use of numbered "implicit" (I suppose that means the type is implicit) fields is better or worse than the other way, but, it wasn't obvious or explained that way in the spec, as far as I saw.

  1. Test Suite

Overall really good, very reassuring to have actual encoded values to test against ๐Ÿ‘.

I made a pr with one small issue I found. I would also like to have seen a message or a prefix payload in some places where they were empty, just to be thorough. I didn't implement all the tests yet, and I see there have been some changes to the latest HEAD so I can't say for sure that's the only one, but still, the vast marjority of it worked, really good!

  1. The Spec

I didn't read it in its entirity from start to finish, so I may have missed details, but I did refer to it extensively.

At a high level, it feels like it's been re-written a bit, half old half new, I tend to see this effect when documentation has patches applied to it, it no longer feels natural, I could be off the mark though.

There are some sections like the following:

A PREFIX-SHA-256 fulfillment is valid iff:

* The size of M, in bytes, is less than or equal to F.maxMessageLength AND
* F.subfulfillment is valid, where the message used for validation of f is M prefixed by F.prefix AND
* D is equal to C

Which left me wondering what D and C were, and I was just now still unable to locate "D".

Hope this is useful!

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.