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

API woes #105

Open
indutny opened this issue Sep 19, 2016 · 5 comments
Open

API woes #105

indutny opened this issue Sep 19, 2016 · 5 comments

Comments

@indutny
Copy link
Owner

indutny commented Sep 19, 2016

Hello everyone!

As many of you know already, (some of) the API for this library kind of sucks. While underlying primitives and implementation are quite fast, method names, arguments and the rest is really messed up.

I would like to ask you to help me define the better sets of APIs that we could move to in a next major version. It is fine to bump the version just to get them, and I don't mind support both for some time.

Let's discuss it here, and once the consensus will be reached - implement it!

cc @ryanxcharles @wanderer @alexeykudinkin @calvinmetcalf @fanatid @braydonf and everyone who is interested!

@dcousens
Copy link

dcousens commented Sep 20, 2016

From using this myself, the only issues I've had a lot of back-and-forth on, having to double check code several times: has been around types.
To date, I've always found a type error to be the major cause for serious issues, and for that some of the methods acceptance of different types often scare me.

I could use a type enforcing library/transpiler, but the API would still be very liberal.

@indutny
Copy link
Owner Author

indutny commented Sep 20, 2016

Personally, I find that the encoding argument for various API functions is too hard to understand.

@dcousens
Copy link

@indutny I agree, which is why I like how you've done the export functions in bn.js, toArray, toBuffer, toArrayLike etc. It is more explicit and tested.
I'd say the same for from* would be ideal.

@calvinmetcalf
Copy link
Contributor

my main issues is just with the Objects, curves vs keys vs signatures it's not always intuitive to me sometimes what's a method on what vs a parameter, e.g. verify is a method of the curve that takes the key as a param, but sign is a method on the key

@swansontec
Copy link

swansontec commented Jan 23, 2018

I worked the elliptic curve API for libbitcion a few years ago. We had similar API woes, so we decided to break away from the original object-oriented approach and try something more "mathematical". This made the library much easier to work with, and was a great success.

Translating the lessons from libbitcoin into a more Javascript-ish API would produce something like:

exports.secp256k1 = {
  Point, // Constructor
  Scalar // Constructor
}
exports.ed25519 = {
  Point, // Constructor
  Scalar // Constructor
}
// Likewise for the other curves...

You might use it like this:

const { secp256k1 } = require('elliptic')

// Scalars are modulo the curve order:
const privateKey = new secp256k1.Scalar('c2e8a67f371340b8a0c982f0854bb699...')

// To go from a scalar to a point, use the Point constructor:
const publicKey = new secp256k1.Point(privateKey)

// Somebody sent us their point, so let's do Diffie-Hellman:
const sharedSecret = privateKey.mul(otherPublicKey)

// Deriving HD addresses for bitcoin? We can add points to scalars:
const step = new secp256k1.Scalar(hmacSha512(bip32.stuff...))
const childPubkey = parentPubKey.add(step)

// We can also add scalars to scalars to get the private keys for those addresses:
const childKey = parentKey.add(step)

Basically, this splits the existing Keypair type into its two mathematical components. Anybody working with an EC math library should already be familiar with these concepts, so the goal is to create less of a mental burden going between math and code.

A Scalar type internally keep track of its corresponding point around for performance reasons, but that's an implementation detail. From the outside, the API matches what somebody would expect mathematically.

The Scalar and Point classes should be immutable, just like Javascript strings and numbers. Instead of modifying the object in-place, you just return a new object with the requested changes. This makes things a lot easier to reason about, and seems to be catching on in modern Javascript API design.

The Scalar type might have a makeSignature method, which accepts a hash to sign. The Point type would then have a checkSignature to match. Basically, all the algorithms that elliptic currently splits out, like EDDSA, just become methods on these two classes. It's not clear whether signatures should also have their own data type, or just be returned directly in DER format.

Other methods might include format converters like toDER and fromDER and information queries like isEven and getX. The methods on the Point class will generally be a subset of the methods on the Scalar class, since private keys are more powerful than public keys.

I know this is a big change, but hopefully this seems reasonable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants