Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

refactor: use @bitcoinerlab/secp256k1 and @noble/secp256k1#1

Merged
alexandrius merged 11 commits into
exodusfrom
mv/bitcoinerlab-secp256k1
Jan 15, 2024
Merged

refactor: use @bitcoinerlab/secp256k1 and @noble/secp256k1#1
alexandrius merged 11 commits into
exodusfrom
mv/bitcoinerlab-secp256k1

Conversation

@mvayngrib

@mvayngrib mvayngrib commented Sep 20, 2023

Copy link
Copy Markdown

todo: verify correctness 😛

@mvayngrib
mvayngrib changed the base branch from master to exodus September 20, 2023 20:23
@mvayngrib
mvayngrib marked this pull request as draft September 20, 2023 20:23
@alexandrius

alexandrius commented Dec 5, 2023

Copy link
Copy Markdown

All tests are successful.

Test Results
yarn test
yarn run v1.19.1
$ standard && mocha


  hdkey
    + fromMasterSeed
      ✓ should properly derive the chain path: m
      ✓ should properly derive the chain path: m/0'
      ✓ should properly derive the chain path: m/0'/1
      ✓ should properly derive the chain path: m/0'/1/2'
      ✓ should properly derive the chain path: m/0'/1/2'/2
      ✓ should properly derive the chain path: m/0'/1/2'/2/1000000000
      ✓ should properly derive the chain path: m
      ✓ should properly derive the chain path: m/0
      ✓ should properly derive the chain path: m/0/2147483647'
      ✓ should properly derive the chain path: m/0/2147483647'/1
      ✓ should properly derive the chain path: m/0/2147483647'/1/2147483646'
      ✓ should properly derive the chain path: m/0/2147483647'/1/2147483646'/2
      > m toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0' toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0'/1 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0'/1/2' toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0'/1/2'/2 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0'/1/2'/2/1000000000 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0/2147483647' toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0/2147483647'/1 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0/2147483647'/1/2147483646' toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
      > m/0/2147483647'/1/2147483646'/2 toJSON() / fromJSON()
        ✓ should return an object read for JSON serialization
    - privateKey
      ✓ should throw an error if incorrect key size
    - publicKey
      ✓ should throw an error if incorrect key size
      ✓ should not throw if key is 33 bytes (compressed)
      ✓ should not throw if key is 65 bytes (not compressed)
    + fromExtendedKey()
      > when private
        ✓ should parse it
      > when public
        ✓ should parse it
        ✓ should parse it without verification
    > when signing
      ✓ should work
    > when deriving public key
      ✓ should work
    > when private key integer is less than 32 bytes
      ✓ should work
    HARDENED_OFFSET
      ✓ should be set
    > when private key has leading zeros
      ✓ will include leading zeros when hashing to derive child
    > when private key is null
      ✓ privateExtendedKey should return null and not throw
     - when the path given to derive contains only the master extended key
      ✓ should return the same hdkey instance
     - when the path given to derive does not begin with master extended key
      ✓ should throw an error
    - after wipePrivateData()
      ✓ should not have private data
      ✓ should have correct data
      ✓ should be able to verify signatures
      ✓ should not throw if called on hdkey without private data
    Deriving a child key does not mutate the internal state
      ✓ should not mutate it when deriving with a private key
      ✓ should not mutate it when deriving without a private key


  45 passing (107ms)

✨  Done in 1.05s.

@mvayngrib Do you think we need to add more test cases?

@alexandrius

alexandrius commented Dec 5, 2023

Copy link
Copy Markdown

The change assumes correctness of @bitcoinerlab/secp256k1, HDKey's API is tested public and private keys. We can add some tests there.

I'd add create separate tests for @exodus/secp256k1 vs @bitcoinerlab/secp256k1.

publicKeyVerify ~> isPoint
publicKeyConvert ~> pointCompress
privateKeyTweakAdd ~> privateAdd
publicKeyTweakAdd ~> pointAddScalar
ecdsaSign ~> sign
ecdsaVerify ~> verify
privateKeyVerify ~> isPrivate
publicKeyCreate ~> pointFromScalar

Will update more later.

cc @andrejborstnik

@RyanZim Can you also chime in here?

@RyanZim

RyanZim commented Dec 6, 2023

Copy link
Copy Markdown

In reviewing the tests here, one thing that sticks out is the publicKey API. It would be great to have actual data correctness checks added here, to verify that the resulting public key is properly compressed/left compressed:

hdkey/test/hdkey.test.js

Lines 61 to 76 in 472ee65

it('should not throw if key is 33 bytes (compressed)', function () {
var priv = secureRandom.randomBuffer(32)
var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true)
assert.equal(pub.length, 33)
var hdkey = new HDKey()
hdkey.publicKey = pub
})
it('should not throw if key is 65 bytes (not compressed)', function () {
var priv = secureRandom.randomBuffer(32)
var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false)
assert.equal(pub.length, 65)
var hdkey = new HDKey()
hdkey.publicKey = pub
})
})

These test additions ideally should be contributed to upstream and backported here.

@alexandrius

Copy link
Copy Markdown

Added more fixtures, point compression test.

@mvayngrib Should I mark "Ready for review"?
Also could you update the head?

@headfire94 headfire94 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

utACK, i rely on unit tests here)

@mvayngrib
mvayngrib marked this pull request as ready for review January 10, 2024 18:04
@mvayngrib

Copy link
Copy Markdown
Author

Also could you update the head?

@alexandrius is this PR behind the exodus branch?

@alexandrius

Copy link
Copy Markdown

@mvayngrib sorry I meant PR description

Comment thread package.json Outdated
{
"name": "@exodus/hdkey",
"version": "2.1.0-exodus.0",
"version": "2.1.1-exodus.1",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

let's not bundle this in with these changes, reverted e1b5ed9

Comment thread test/fixtures/hdkey.json
"private": "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"
},
{
"seed": "69afbf0608755b3480ca7314c145695c64f973c988790eabb165d19809c7991acecfe4518b1456f274e171d496ddd044942278577b6efcdb69a6374d5341ea0a",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@alexandrius what did u use to generate these?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Extracted from:
src/_local_modules/keys/derive.js createKeyAccess

@RyanZim RyanZim left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Quick utACK; did not verify correctness.

Can we get these new tests contributed to upstream?

@mvayngrib

mvayngrib commented Jan 10, 2024

Copy link
Copy Markdown
Author

I'd add create separate tests for @exodus/secp256k1 vs @bitcoinerlab/secp256k1.

publicKeyVerify ~> isPoint
publicKeyConvert ~> pointCompress
privateKeyTweakAdd ~> privateAdd
publicKeyTweakAdd ~> pointAddScalar
ecdsaSign ~> sign
ecdsaVerify ~> verify
privateKeyVerify ~> isPrivate
publicKeyCreate ~> pointFromScalar

comparing the two libs, they all look correct to me (the "tweak" methods are in-place vs immutable, but the way we use them it doesn't matter). @alexandrius did u still want to add unit tests for these?

@alexandrius

Copy link
Copy Markdown

@mvayngrib I don't think we need anything else, it was quick initial thought

@alexandrius

Copy link
Copy Markdown

@RyanZim will do

@mvayngrib

Copy link
Copy Markdown
Author

@mvayngrib I don't think we need anything else, it was quick initial thought

for something this core/sensitive, i think u had the right idea when u said to add them 😅

@alexandrius

Copy link
Copy Markdown

for something this core/sensitive, i think u had the right idea when u said to add them 😅

I can backport tests to previous version. I did on my end was successful

@alexandrius

Copy link
Copy Markdown

Should we go ahead with merge and publish?

@alexandrius

Copy link
Copy Markdown

@RyanZim BTW created pull request to upstream
cryptocoinjs#60

@mvayngrib

Copy link
Copy Markdown
Author

I can backport tests to previous version. I did on my end was successful

sorry, not following. I mean can we add some unit tests for the underlying functions u listed here #1 (comment)

@alexandrius

Copy link
Copy Markdown

@mvayngrib I can but I don't think we should do that since we also have this: ExodusMovement/secp256k1-node#1

@mvayngrib

Copy link
Copy Markdown
Author

ah, so the unit tests there will make sure those methods match correctly? sgtm

@mvayngrib

Copy link
Copy Markdown
Author

@alexandrius sorry, maybe i'm getting confused. With that PR, why do we even need this one?

@alexandrius

Copy link
Copy Markdown

@mvayngrib when this PR was created I think we thought only this PR would be enough to match perf. Another point is to incrementally jump on noble

@mvayngrib

Copy link
Copy Markdown
Author

could u walk me through the plan, is it this?

  1. finish unit tests in that PR to make us comfortable with this PR
  2. merge + release + use this
  3. sometime later, revert this and use that PR instead?

@alexandrius

alexandrius commented Jan 15, 2024

Copy link
Copy Markdown

@mvayngrib basically yes. Except swap 1st and 2nd steps. We have all tests to check hdkey correctness comparing the methods above do not belong in here

@mvayngrib

Copy link
Copy Markdown
Author

as long as we don't ship this to users before we have that unit test suite, i'm okay with it

@alexandrius

Copy link
Copy Markdown

@mvayngrib without that PR we won't be able to ship to users anyway.
However I would argue testing those methods do not belong here anyway since we have hdkey correctness checks

@alexandrius
alexandrius merged commit a9882d7 into exodus Jan 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants