feat: add borsh utilities and public key support (#17239)

* feat: add borsh utilities and public key support

* fix: make bn internal for flow

* fix: add Buffer import in borsh file
This commit is contained in:
Jon Cinque 2021-05-18 19:33:06 +02:00 committed by GitHub
parent 75335b4f58
commit 00e198d169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 140 additions and 13 deletions

View File

@ -11,6 +11,7 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"bn.js": "^5.0.0",
"borsh": "^0.4.0",
"bs58": "^4.0.1",
"buffer": "6.0.1",
"buffer-layout": "^1.2.0",
@ -4682,6 +4683,25 @@
"node": ">=0.6"
}
},
"node_modules/borsh": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz",
"integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==",
"dependencies": {
"@types/bn.js": "^4.11.5",
"bn.js": "^5.0.0",
"bs58": "^4.0.0",
"text-encoding-utf-8": "^1.0.2"
}
},
"node_modules/borsh/node_modules/@types/bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/bottleneck": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
@ -4799,6 +4819,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz",
"integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==",
"hasInstallScript": true,
"optional": true,
"dependencies": {
"node-gyp-build": "^4.2.0"
@ -17828,6 +17849,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz",
"integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==",
"hasInstallScript": true,
"dependencies": {
"elliptic": "^6.5.2",
"node-addon-api": "^2.0.0",
@ -19672,6 +19694,11 @@
"node": "*"
}
},
"node_modules/text-encoding-utf-8": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
"integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="
},
"node_modules/text-extensions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",
@ -20157,6 +20184,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.4.tgz",
"integrity": "sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q==",
"hasInstallScript": true,
"optional": true,
"dependencies": {
"node-gyp-build": "^4.2.0"
@ -24684,6 +24712,27 @@
}
}
},
"borsh": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz",
"integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==",
"requires": {
"@types/bn.js": "^4.11.5",
"bn.js": "^5.0.0",
"bs58": "^4.0.0",
"text-encoding-utf-8": "^1.0.2"
},
"dependencies": {
"@types/bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
"requires": {
"@types/node": "*"
}
}
}
},
"bottleneck": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
@ -36400,6 +36449,11 @@
}
}
},
"text-encoding-utf-8": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
"integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="
},
"text-extensions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",

View File

@ -64,6 +64,7 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"bn.js": "^5.0.0",
"borsh": "^0.4.0",
"bs58": "^4.0.1",
"buffer": "6.0.1",
"buffer-layout": "^1.2.0",

View File

@ -52,6 +52,7 @@ function generateConfig(configType, format) {
config.external = [
/@babel\/runtime/,
'bn.js',
'borsh',
'bs58',
'buffer-layout',
'crypto-hash',
@ -81,6 +82,7 @@ function generateConfig(configType, format) {
config.external = [
/@babel\/runtime/,
'bn.js',
'borsh',
'bs58',
'buffer',
'buffer-layout',

View File

@ -16,6 +16,7 @@ export * from './transaction';
export * from './validator-info';
export * from './vote-account';
export * from './sysvar';
export * from './util/borsh-schema';
export * from './util/send-and-confirm-transaction';
export * from './util/send-and-confirm-raw-transaction';
export * from './util/cluster';

View File

@ -4,6 +4,7 @@ import nacl from 'tweetnacl';
import {sha256} from 'crypto-hash';
import {Buffer} from 'buffer';
import {Struct, SOLANA_SCHEMA} from './util/borsh-schema';
import {toBuffer} from './util/to-buffer';
/**
@ -11,10 +12,27 @@ import {toBuffer} from './util/to-buffer';
*/
export const MAX_SEED_LENGTH = 32;
type PublicKeyInitData =
| number
| string
| Buffer
| Uint8Array
| Array<number>
| PublicKeyData;
type PublicKeyData = {
/** @internal */
_bn: BN;
};
function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
return (value as PublicKeyData)._bn !== undefined;
}
/**
* A public key
*/
export class PublicKey {
export class PublicKey extends Struct {
/** @internal */
_bn: BN;
@ -22,20 +40,25 @@ export class PublicKey {
* Create a new PublicKey object
* @param value ed25519 public key as buffer or base-58 encoded string
*/
constructor(value: number | string | Buffer | Uint8Array | Array<number>) {
if (typeof value === 'string') {
// assume base 58 encoding by default
const decoded = bs58.decode(value);
if (decoded.length != 32) {
constructor(value: PublicKeyInitData) {
super({});
if (isPublicKeyData(value)) {
this._bn = value._bn;
} else {
if (typeof value === 'string') {
// assume base 58 encoding by default
const decoded = bs58.decode(value);
if (decoded.length != 32) {
throw new Error(`Invalid public key input`);
}
this._bn = new BN(decoded);
} else {
this._bn = new BN(value);
}
if (this._bn.byteLength() > 32) {
throw new Error(`Invalid public key input`);
}
this._bn = new BN(decoded);
} else {
this._bn = new BN(value);
}
if (this._bn.byteLength() > 32) {
throw new Error(`Invalid public key input`);
}
}
@ -167,6 +190,11 @@ export class PublicKey {
}
}
SOLANA_SCHEMA.set(PublicKey, {
kind: 'struct',
fields: [['_bn', 'u256']],
});
// @ts-ignore
let naclLowLevel = nacl.lowlevel;

View File

@ -0,0 +1,34 @@
import {Buffer} from 'buffer';
import {serialize, deserialize} from 'borsh';
// Class wrapping a plain object
export class Struct {
constructor(properties: any) {
Object.assign(this, properties);
}
encode(): Buffer {
return Buffer.from(serialize(SOLANA_SCHEMA, this));
}
static decode(data: Buffer): any {
return deserialize(SOLANA_SCHEMA, this, data);
}
}
// Class representing a Rust-compatible enum, since enums are only strings or
// numbers in pure JS
export class Enum extends Struct {
enum: string = '';
constructor(properties: any) {
super(properties);
if (Object.keys(properties).length !== 1) {
throw new Error('Enum can only take single value');
}
Object.keys(properties).map(key => {
this.enum = key;
});
}
}
export const SOLANA_SCHEMA: Map<Function, any> = new Map();

View File

@ -342,4 +342,11 @@ describe('PublicKey', function () {
);
expect(PublicKey.isOnCurve(offCurve.toBuffer())).to.be.false;
});
it('canBeSerializedWithBorsh', () => {
const publicKey = Keypair.generate().publicKey;
const encoded = publicKey.encode();
const decoded = PublicKey.decode(encoded);
expect(decoded.equals(publicKey)).to.be.true;
});
});