Skip to content

Commit

Permalink
ts: fix translateAddress should not rely on PublicKey constructor name
Browse files Browse the repository at this point in the history
Since anchor is used in web projects which will minify/mangle function
names, checking an input object for a custom constructor name is unsafe.

Moreover, checking the type of the input Address is unnecessary since
`PublicKey`'s constructor handles:
- `string`
- `array`
- object with a `{ _bn }` shape (like `PublicKey` itself)

For additional safety, a unit test should be added to prevent `PublicKey`
to stop supporting this unexpectedly.

See `PublicKey` implementation:

```
/**
 * JSON object representation of PublicKey class
 */
export type PublicKeyData = {
  /** @internal */
  _bn: BN;
};

function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
  return (value as PublicKeyData)._bn !== undefined;
}

export class PublicKey extends Struct {
  /** @internal */
  _bn: BN;

  /**
   * Create a new PublicKey object
   * @param value ed25519 public key as buffer or base-58 encoded string
   */
  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`);
      }
    }
  }
```
  • Loading branch information
0xCryptoSheik committed Dec 13, 2021
1 parent fcb07eb commit cad856e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
9 changes: 1 addition & 8 deletions ts/src/program/common.ts
Expand Up @@ -55,14 +55,7 @@ export function validateAccounts(

// Translates an address to a Pubkey.
export function translateAddress(address: Address): PublicKey {
if (typeof address === "string") {
const pk = new PublicKey(address);
return pk;
} else if (address.constructor.prototype.constructor.name === "PublicKey") {
return address;
} else {
throw new Error("Given address is not a PublicKey nor a string.");
}
return new PublicKey(address);
}

/**
Expand Down
56 changes: 56 additions & 0 deletions ts/tests/program-common.spec.ts
@@ -0,0 +1,56 @@
import BN from "bn.js";
import bs58 from "bs58";
import { PublicKey } from "@solana/web3.js";

import { translateAddress } from "../src/program/common";

describe("program/common", () => {
describe("translateAddress", () => {
it("should accept a valid string address", () => {
const address = "11111111111111111111111111111111";

const func = () => translateAddress(address);
expect(func).not.toThrow();

const output = func();
expect(output).toBeInstanceOf(PublicKey);
expect(new PublicKey(address).equals(output)).toBeTruthy();
});

it("should accept a PublicKey address", () => {
const publicKey = new PublicKey("11111111111111111111111111111111");

const func = () => translateAddress(publicKey);
expect(func).not.toThrow();

const output = func();
expect(output).toBeInstanceOf(PublicKey);
expect(new PublicKey(publicKey).equals(output)).toBe(true);
});

it("should accept an object with a PublicKey shape { _bn }", () => {
const obj = ({
_bn: new BN(bs58.decode("11111111111111111111111111111111")),
} as any) as PublicKey;
const func = () => translateAddress(obj);

expect(func).not.toThrow();
const output = func();

expect(output).toBeInstanceOf(PublicKey);
expect(new PublicKey(obj).equals(output)).toBe(true);
});

it("should not accept an invalid string address", () => {
const invalid = "invalid";
const func = () => translateAddress(invalid);
expect(func).toThrow();
});

it("should not accept an invalid object", () => {
const invalid = {} as PublicKey;
const func = () => translateAddress(invalid);
expect(func).toThrow();
});
});
});

0 comments on commit cad856e

Please sign in to comment.