Skip to content

Commit

Permalink
feat: class Account (#1178)
Browse files Browse the repository at this point in the history
* first commit

* first commit

* feat: changes

* feat: small change

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: more changes, still tests to add

* feat: added key tests

* feat: increased coverage

* feat: vcdm diagram modified

* feat: account and externallyownedaccount

* feat: account and externallyownedaccount

* feat: account and externallyownedaccount

* feat: added test

* feat: added test

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more coverage

* feat: more coverage

* feat: updated class diagram

* feat: updated comments in parent interface

* feat: removed TODOs as per code review
  • Loading branch information
freemanzMrojo authored Aug 21, 2024
1 parent 353aad8 commit 755ce19
Show file tree
Hide file tree
Showing 13 changed files with 388 additions and 11 deletions.
16 changes: 16 additions & 0 deletions docs/diagrams/architecture/vcdm.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
```mermaid
classDiagram
class Account {
<<abstract>>
#address: Address
#balance: Currency
#mnemonic: Mnemonic
}
class ExternallyOwnedAccount
class Contract
class Currency {
<<interface>>
}
class Address {
+string checksum(HexUInt huint)$
+boolean isValid(string exp)$
Expand Down Expand Up @@ -58,6 +69,11 @@ classDiagram
+boolean isEqual(~T~ that)
+boolean isNumber()
}
Account "1" ..|> "1" Address : has
Account "1" ..|> "1" Mnemonic : has
Account "1" ..|> "1" Currency : has
Account <|-- ExternallyOwnedAccount
Account <|-- Contract
Hash <|.. Blake2b256
Hash <|.. Keccak256
Hash <|.. Sha256
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/hash/Blake2b256.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Hex, HexUInt, Txt, type Hash } from '../vcdm';
import { InvalidOperation } from '@vechain/sdk-errors';
import { blake2b as nh_blake2b } from '@noble/hashes/blake2b';
import { InvalidOperation } from '@vechain/sdk-errors';
import { Hex, HexUInt, Txt, type Hash } from '../vcdm';
/**
* Represents the result of an [BLAKE](https://en.wikipedia.org/wiki/BLAKE_(hash_function)) [BlAKE2B 256](https://www.blake2.net/) hash operation.
*
Expand Down Expand Up @@ -38,7 +38,7 @@ class Blake2b256 extends HexUInt implements Hash {
}
}

// TODO: Backwards compatibility, remove in future release.
// Backwards compatibility, remove in future release #1184

const blake2b256 = (
hex: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hash/Keccak256.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Keccak256 extends HexUInt implements Hash {
}
}

// TODO: Backwards compatibility, remove in future release.
// Backwards compatibility, remove in future release #1184

const keccak256 = (
hex: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hash/Sha256.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Sha256 extends HexUInt implements Hash {
}
}

// TODO: Backwards compatibility, remove in future release.
// Backwards compatibility, remove in future release #1184

const sha256 = (
hex: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/vcdm/Address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class Address extends HexUInt {
}
}

// TODO: Backwards compatibility, remove when it is matured enough
// Backwards compatibility, remove when it is matured enough #1184

const addressUtils = {
fromPrivateKey: (privateKey: Uint8Array): string =>
Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/vcdm/Mnemonic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class Mnemonic extends Txt {
}
}

// TODO: Legacy method, probably should be part of a Private Key class (ofMnemonic)
// Legacy method, probably should be part of a Private Key class (ofMnemonic) #1122
/**
* Derives a private key from a given list of
* [BIP39 Mnemonic Words](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
Expand Down Expand Up @@ -211,9 +211,18 @@ class Mnemonic extends Txt {
const wordsToValidate = Array.isArray(words) ? words.join(' ') : words;
return validateMnemonic(wordsToValidate, wordlist);
}

/**
* Returns an empty string to prevent printing the mnemonic.
*
* @returns {string} An empty string
*/
public toString(): string {
return '';
}
}

// TODO: Backwards compatibility, remove in future versions
// Backwards compatibility, remove in future versions #1184

const mnemonic = {
deriveAddress: (words: string[], path: string = 'm/0'): string =>
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/vcdm/Revision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Revision extends Txt {
}
}

// TODO: Backwards compatibility, remove when it is matured enough
// Backwards compatibility, remove when it is matured enough #1184

const revisionUtils = {
isRevisionAccount: (revision: string | number): boolean =>
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/vcdm/VeChainDataModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface VeChainDataModel<T> {
// Properties.
/**
* Return this instance cast to a big integer value
* @throws InvalidCastType if this object can't cast to a big integer.
* @throws InvalidOperation if this object can't cast to a big integer.
*/
get bi(): bigint;

Expand All @@ -19,7 +19,7 @@ export interface VeChainDataModel<T> {

/**
* Return this object cast to number value.
* @throws InvalidCastType if this object can't cast to a big integer.
* @throws InvalidOperation if this object can't cast to a big integer.
*/
get n(): number;

Expand Down
121 changes: 121 additions & 0 deletions packages/core/src/vcdm/account/Account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { InvalidOperation } from '@vechain/sdk-errors';
import { type Address } from '../Address';
import { type Currency } from '../Currency';
import { type VeChainDataModel } from '../VeChainDataModel';

type AccountType = 'EOA' | 'Contract';

/**
* Represents a VeChain account.
*
* @implements {VeChainDataModel<Account>}
*/
abstract class Account implements VeChainDataModel<Account> {
public readonly address: Address;
public readonly balance: Currency;
// Replace the string array with a Transaction class #1162
public readonly transactions: string[];
protected abstract get type(): AccountType;

constructor(address: Address, balance: Currency, transactions?: string[]) {
this.address = address;
this.balance = balance;
this.transactions = transactions ?? [];
}

/**
* Throws an exception because the account cannot be represented as a big integer.
* @returns {bigint} The BigInt representation of the account.
* @throws {InvalidOperation} The account cannot be represented as a bigint.
* @override {@link VeChainDataModel#bi}
* @remark The conversion to BigInt is not supported for an account.
*/
public get bi(): bigint {
throw new InvalidOperation(
'Account.bi',
'There is no big integer representation for an account.',
{ data: '' }
);
}

/**
* Throws an exception because the account cannot be represented as a byte array.
* @returns {Uint8Array} The byte array representation of the account.
* @throws {InvalidOperation} The account cannot be represented as a byte array.
* @override {@link VeChainDataModel#bytes}
* @remark The conversion to byte array is not supported for an account.
*/
public get bytes(): Uint8Array {
throw new InvalidOperation(
'Account.bytes',
'There is no bytes representation for an account.',
{ data: '' }
);
}

/**
* Throws an exception because the account cannot be represented as a number.
* @returns {bigint} The number representation of the account.
* @throws {InvalidOperation} The account cannot be represented as a number.
* @override {@link VeChainDataModel#n}
* @remark The conversion to number is not supported for an account.
*/
public get n(): number {
throw new InvalidOperation(
'Account.n',
'There is no number representation for an account.',
{ data: '' }
);
}

/**
* Adds a transaction to the account.
* @param {string} transaction The transaction to add.
*/
public addTransaction(transaction: string): void {
// Replace body once Transaction class is implemented #1162
this.transactions.push(transaction);
}

/**
* Compare this instance with `that` in a meaningful way.
*
* @param {Account} that object to compare.
* @return a negative number if `this` < `that`, zero if `this` = `that`, a positive number if `this` > that`.
* @override {@link VeChainDataModel#compareTo}
*/
public compareTo(that: Account): number {
const typeCompareTo = this.type.localeCompare(that.type);
if (typeCompareTo !== 0) {
return typeCompareTo;
}
const addressCompareTo = this.address.compareTo(that.address);
if (addressCompareTo !== 0) {
return addressCompareTo;
}
return this.balance.compareTo(that.balance);
}

/**
* Checks if the given value is equal to the current instance.
*
* @param {Account} that - The value to compare.
* @returns {boolean} - True if the values are equal, false otherwise.
* @override {@link VeChainDataModel#isEqual}
*/
public isEqual(that: Account): boolean {
return this.compareTo(that) === 0;
}

/**
* Returns a string representation of the account.
*
* @returns {string} A string representation of the account.
*/
public toString(): string {
return `${this.type} Address: ${this.address.toString()} Balance: ${this.balance.bi.toString()}`;
}
}

export { Account };
export type { AccountType };
83 changes: 83 additions & 0 deletions packages/core/src/vcdm/account/ExternallyOwnedAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { InvalidDataType } from '@vechain/sdk-errors';
import { Address } from '../Address';
import { type Currency } from '../Currency';
import { type Mnemonic } from '../Mnemonic';
import { Account, type AccountType } from './Account';

/**
* Represents an externally owned account (EOA) on the VeChainThor blockchain.
*
* @extends {Account}
*/
class ExternallyOwnedAccount extends Account {
readonly type: AccountType = 'EOA';

private readonly mnemonic: Mnemonic;
// Review whether we need to add the SECP256k1 key pair here #1122

constructor(
address: Address,
balance: Currency,
mnemonic: Mnemonic,
transactions?: string[]
) {
if (!ExternallyOwnedAccount.isValid(address, mnemonic)) {
throw new InvalidDataType(
'ExternallyOwnedAccount.constructor',
'The address and mnemonic do not match.',
{ address: address.toString() }
);
}
super(address, balance, transactions);
this.mnemonic = mnemonic;
}

/**
* Validates that the given address and mnemonic's address match.
* @param {Address} address Address to validate.
* @param {Mnemonic} mnemonic Mnemonic to validate.
* @returns {boolean} True if the address and mnemonic's address match, false otherwise.
*/
public static isValid(address: Address, mnemonic: Mnemonic): boolean {
const addressFromMnemonic = Address.ofMnemonic(mnemonic);
return address.isEqual(addressFromMnemonic);
}

/**
* Compares the current ExternallyOwnedAccount object with the given ExternallyOwnedAccount object.
* @param {ExternallyOwnedAccount} that - The ExternallyOwnedAccount object to compare with.
* @return {number} - A negative number if the current object is less than the given object,
* zero if they are equal, or a positive number if the current object is greater.
* @override {@link Account#compareTo}
* @remark The comparison is based on the address and mnemonic of the ExternallyOwnedAccount.
*/
public compareTo(that: ExternallyOwnedAccount): number {
const accountCompareTo = super.compareTo(that);
if (accountCompareTo !== 0) {
return accountCompareTo;
}
return this.mnemonic.compareTo(that.mnemonic);
}

/**
* Checks if the current ExternallyOwnedAccount object is equal to the given ExternallyOwnedAccount object.
* @param {ExternallyOwnedAccount} that - The ExternallyOwnedAccount object to compare with.
* @return {boolean} - True if the objects are equal, false otherwise.
* @override {@link Account#isEqual}
* @remark The comparison is based on the address and mnemonic of the ExternallyOwnedAccount.
*/
public isEqual(that: ExternallyOwnedAccount): boolean {
return super.isEqual(that) && this.mnemonic.isEqual(that.mnemonic);
}

/**
* Returns a string representation of the ExternallyOwnedAccount.
*
* @returns {string} A string representation of the ExternallyOwnedAccount.
*/
public toString(): string {
return `${super.toString()} Mnemonic: ${this.mnemonic.toString()}`;
}
}

export { ExternallyOwnedAccount };
2 changes: 2 additions & 0 deletions packages/core/src/vcdm/account/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Account';
export * from './ExternallyOwnedAccount';
1 change: 1 addition & 0 deletions packages/core/src/vcdm/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './account';
export * from './Address';
export * from './Currency';
export * from './Hash';
Expand Down
Loading

1 comment on commit 755ce19

@github-actions
Copy link

Choose a reason for hiding this comment

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

Test Coverage

Summary

Lines Statements Branches Functions
Coverage: 99%
99.12% (3640/3672) 97.14% (953/981) 99.18% (728/734)
Title Tests Skipped Failures Errors Time
core 542 0 💤 0 ❌ 0 🔥 1m 11s ⏱️
network 684 0 💤 0 ❌ 0 🔥 4m 4s ⏱️
errors 40 0 💤 0 ❌ 0 🔥 13.991s ⏱️

Please sign in to comment.