Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aelf-sdk",
"version": "3.5.0-beta.9",
"version": "3.5.0-beta.10",
"description": "aelf-sdk js library",
"type": "module",
"main": "dist/aelf.cjs",
Expand Down Expand Up @@ -93,14 +93,14 @@
"@aws-crypto/sha256-js": "^5.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/runtime": "^7.4.5",
"@scure/base": "^2.0.0",
"@scure/bip32": "^2.0.0",
"@typescript-eslint/parser": "^5.47.1",
"babel-plugin-rewire": "^1.2.0",
"bignumber.js": "^9.0.0",
"bip39": "^3.0.2",
"bn.js": "^5.2.1",
"browserify-cipher": "^1.0.1",
"bs58": "^4.0.1",
"buffer": "^5.2.1",
"crypto-js": "^4.2.0",
"elliptic": "^6.4.1",
Expand Down
7 changes: 5 additions & 2 deletions src/util/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
* @date 2015
*/
import descriptor from '@aelfqueen/protobufjs/ext/descriptor/index.js';
import bs58 from 'bs58';
// import bs58 from 'bs58';
import { base58 as bs58 } from '@scure/base';
import { base58 } from './utils.js';

const getByteCountByAddress = base58Str => {
// convert a Base58 string to a binary array and get its byte count
const buffer = bs58.decode(base58Str);
// @scure/base returns Uint8Array, convert to Buffer
const decoded = bs58.decode(base58Str);
const buffer = Buffer.from(decoded);
// get byte
const byteCount = buffer.length;
// last four digits are the checksum
Expand Down
17 changes: 12 additions & 5 deletions src/util/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*/

import BigNumber from 'bignumber.js';
import bs58 from 'bs58';
// import bs58 from 'bs58';
import { base58 as bs58 } from '@scure/base';
import { UNIT_MAP, UNSIGNED_256_INT } from '../common/unitConstants.js';
import sha256 from './sha256.js';

Expand All @@ -21,10 +22,13 @@ export const base58 = {
hash = Buffer.from(sha256(result), 'hex');
hash = Buffer.from(sha256(hash), 'hex');
hash = Buffer.from(result.toString('hex') + hash.slice(0, 4).toString('hex'), 'hex');
return bs58.encode(hash);
// Convert Buffer to Uint8Array for @scure/base
return bs58.encode(new Uint8Array(hash));
},
decode(str, encoding) {
const buffer = Buffer.from(bs58.decode(str));
// @scure/base returns Uint8Array, convert to Buffer
const decoded = bs58.decode(str);
const buffer = Buffer.from(decoded);
let data = buffer.slice(0, -4);
let hash = data;
hash = Buffer.from(sha256(hash), 'hex');
Expand All @@ -47,10 +51,13 @@ export const chainIdConvertor = {
const bufferTemp = Buffer.alloc(4);
bufferTemp.writeInt32LE(`0x${chainId.toString('16')}`, 0);
const bytes = Buffer.concat([bufferTemp], 3);
return bs58.encode(bytes);
// Convert Buffer to Uint8Array for @scure/base
return bs58.encode(new Uint8Array(bytes));
},
base58ToChainId(base58String) {
return Buffer.concat([bs58.decode(base58String)], 4).readInt32LE(0);
// @scure/base returns Uint8Array, convert to Buffer
const decoded = bs58.decode(base58String);
return Buffer.concat([Buffer.from(decoded)], 4).readInt32LE(0);
}
};

Expand Down
184 changes: 184 additions & 0 deletions test/compatibility/bs58-compatibility.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* @file bs58 compatibility tests
* @description Tests to verify compatibility between bs58 and @scure/base
* This ensures that both implementations produce identical results
*/

import { describe, it, expect } from 'vitest';
import bs58 from 'bs58';
import { base58 as scureBase58 } from '@scure/base';

describe('bs58 Compatibility Tests', () => {
// Test data - various types of input
const testCases = [
{
name: 'simple hex string',
input: '48656c6c6f20576f726c64', // "Hello World" in hex
encoding: 'hex'
},
{
name: 'empty buffer',
input: '',
encoding: 'hex'
},
{
name: 'single byte',
input: 'ff',
encoding: 'hex'
},
{
name: '32 bytes (typical hash)',
input: 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456',
encoding: 'hex'
},
{
name: 'address-like data (33 bytes)',
input: 'a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef12345678',
encoding: 'hex'
},
{
name: 'chain ID data (3 bytes)',
input: '123456',
encoding: 'hex'
}
];

describe('Direct encoding/decoding compatibility', () => {
testCases.forEach(({ name, input, encoding }) => {
it(`should produce identical results for ${name}`, () => {
const buffer = Buffer.from(input, encoding);

// Test bs58
const bs58Encoded = bs58.encode(buffer);
const bs58Decoded = bs58.decode(bs58Encoded);

// Test @scure/base with Uint8Array
const uint8Array = new Uint8Array(buffer);
const scureEncoded = scureBase58.encode(uint8Array);
const scureDecoded = scureBase58.decode(scureEncoded);

// Results should be identical
expect(scureEncoded).toBe(bs58Encoded);
expect(Buffer.from(scureDecoded).toString('hex')).toBe(Buffer.from(bs58Decoded).toString('hex'));
});
});
});

describe('Buffer vs Uint8Array conversion', () => {
it('should handle Buffer to Uint8Array conversion correctly', () => {
const testData = '48656c6c6f20576f726c64';
const buffer = Buffer.from(testData, 'hex');
const uint8Array = new Uint8Array(buffer);

// Both should produce same result
const bs58Result = bs58.encode(buffer);
const scureResult = scureBase58.encode(uint8Array);

expect(scureResult).toBe(bs58Result);
});

it('should handle Uint8Array to Buffer conversion correctly', () => {
const testData = '48656c6c6f20576f726c64';
const uint8Array = new Uint8Array(Buffer.from(testData, 'hex'));

const encoded = scureBase58.encode(uint8Array);
const decoded = scureBase58.decode(encoded);

// Convert back to Buffer for comparison
const buffer = Buffer.from(decoded);
expect(buffer.toString('hex')).toBe(testData);
});
});

describe('Edge cases', () => {
it('should handle zero bytes correctly', () => {
const zeroBuffer = Buffer.alloc(10, 0);
const zeroUint8Array = new Uint8Array(zeroBuffer);

const bs58Result = bs58.encode(zeroBuffer);
const scureResult = scureBase58.encode(zeroUint8Array);

expect(scureResult).toBe(bs58Result);
});

it('should handle maximum byte values correctly', () => {
const maxBuffer = Buffer.alloc(10, 0xff);
const maxUint8Array = new Uint8Array(maxBuffer);

const bs58Result = bs58.encode(maxBuffer);
const scureResult = scureBase58.encode(maxUint8Array);

expect(scureResult).toBe(bs58Result);
});

it('should handle mixed byte values correctly', () => {
const mixedBuffer = Buffer.from([0x00, 0xff, 0x7f, 0x80, 0x01, 0xfe]);
const mixedUint8Array = new Uint8Array(mixedBuffer);

const bs58Result = bs58.encode(mixedBuffer);
const scureResult = scureBase58.encode(mixedUint8Array);

expect(scureResult).toBe(bs58Result);
});
});

describe('Round-trip compatibility', () => {
testCases.forEach(({ name, input, encoding }) => {
it(`should maintain data integrity through round-trip for ${name}`, () => {
const originalBuffer = Buffer.from(input, encoding);
const originalUint8Array = new Uint8Array(originalBuffer);

// Encode with @scure/base
const encoded = scureBase58.encode(originalUint8Array);

// Decode with @scure/base
const decoded = scureBase58.decode(encoded);
const resultBuffer = Buffer.from(decoded);

// Should match original
expect(resultBuffer.toString('hex')).toBe(originalBuffer.toString('hex'));
});
});
});

describe('Error handling compatibility', () => {
it('should handle invalid base58 strings similarly', () => {
const invalidStrings = [
'0', // contains invalid character
'O', // contains invalid character
'I', // contains invalid character
'l', // contains invalid character
'invalid!@#', // contains invalid characters
];

invalidStrings.forEach(invalidStr => {
expect(() => bs58.decode(invalidStr)).toThrow();
expect(() => scureBase58.decode(invalidStr)).toThrow();
});
});

it('should handle empty string consistently', () => {
// Empty string might be handled differently by different libraries
const emptyStr = '';

// Test if both throw or both don't throw
let bs58Throws = false;
let scureThrows = false;

try {
bs58.decode(emptyStr);
} catch (e) {
bs58Throws = true;
}

try {
scureBase58.decode(emptyStr);
} catch (e) {
scureThrows = true;
}

// Both should behave the same way
expect(bs58Throws).toBe(scureThrows);
});
});
});
Loading