]> git.codecow.com Git - libnemo.git/commitdiff
Create initial chaining functions for Block class.
authorChris Duncan <chris@zoso.dev>
Tue, 5 Aug 2025 21:50:50 +0000 (14:50 -0700)
committerChris Duncan <chris@zoso.dev>
Tue, 5 Aug 2025 21:50:50 +0000 (14:50 -0700)
src/lib/block.ts

index f97215be67d9045873a499ebe65ab0324dc344c9..b70e1d793a9a4cf9fe0af3aa2e38f50114937956 100644 (file)
@@ -17,9 +17,9 @@ import { Rpc } from './rpc'
 abstract class Block {
        account: Account
        type: 'state' = 'state'
-       abstract subtype: 'send' | 'receive' | 'change'
+       abstract subtype?: 'send' | 'receive' | 'change'
        abstract previous: string
-       abstract representative: Account
+       abstract representative?: Account
        abstract balance: bigint
        abstract link: string
        abstract signature?: string
@@ -44,6 +44,9 @@ abstract class Block {
        * @returns {Promise<string>} Block data hashed to a byte array
        */
        get hash (): string {
+               if (this.representative == null) {
+                       throw new Error('Block hash missing representative')
+               }
                const data = [
                        PREAMBLE,
                        this.account.publicKey,
@@ -67,7 +70,7 @@ abstract class Block {
                        "type": this.type,
                        "account": this.account.address,
                        "previous": this.previous,
-                       "representative": this.representative.address,
+                       "representative": this.representative?.address ?? '',
                        "balance": this.balance.toString(),
                        "link": this.link,
                        "signature": this.signature ?? '',
@@ -75,6 +78,24 @@ abstract class Block {
                }
        }
 
+       /**
+       * Set the subtype and link to configure this as a change representative block.
+       *
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       change (): Block {
+               if (this.subtype != null) {
+                       throw new Error(`Block is already configured as ${this.subtype}`)
+               }
+               try {
+                       this.subtype = 'change'
+                       return this
+               } catch (err) {
+                       this.subtype = undefined
+                       throw new TypeError('Failed to configure send block', { cause: err })
+               }
+       }
+
        /**
        * Calculates proof-of-work using a pool of Web Workers.
        *
@@ -126,6 +147,59 @@ abstract class Block {
                return res.hash
        }
 
+       /**
+       * Set the amount of nano that this block will send to a recipient account.
+       *
+       * @param {bigint} amount - Amount to send to recipient in raw
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       send (amount: bigint): Block
+       /**
+       * Set the amount of nano that this block will send to a recipient account.
+       *
+       * @param {number} amount - Amount to send to recipient in nano (10³⁰ raw)
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       send (amount: number): Block
+       /**
+       * Set the amount of nano that this block will send to a recipient account.
+       *
+       * @param {string} amount - Amount to send to recipient in raw
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       send (amount: string): Block
+       send (amount: unknown): Block {
+               const currentBalance = this.balance
+               try {
+                       if (this.subtype != null) {
+                               throw new Error(`Block is already configured as ${this.subtype}`)
+                       }
+                       this.subtype = 'send'
+                       switch (typeof amount) {
+                               case 'bigint': {
+                                       this.balance -= amount
+                                       break
+                               }
+                               case 'number': {
+                                       this.balance -= BigInt(amount) * (1n << 30n)
+                                       break
+                               }
+                               case 'string': {
+                                       this.balance -= BigInt(amount)
+                                       break
+                               }
+                               default: {
+                                       throw new TypeError('Invalid amount')
+                               }
+                       }
+                       return this
+               } catch (err) {
+                       this.subtype = undefined
+                       this.balance = currentBalance
+                       throw new TypeError('Failed to configure send block', { cause: err })
+               }
+       }
+
        /**
        * Signs the block using a private key. If successful, the result is stored in
        * the object's `signature` property.
@@ -176,6 +250,52 @@ abstract class Block {
                }
        }
 
+       /**
+       * Set the recipient of a send block or the target representative of a change
+       * block.
+       *
+       * @param {string} account - Address or public key of Account to target
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       to (account: string): Block
+       /**
+       * Set the recipient of a send block or the target representative of a change
+       * block.
+       *
+       * @param {Account} account - Account to target
+       * @returns {Block} This block so that additional calls can be chained
+       */
+       to (account: Account): Block
+       to (account: unknown): Block {
+               try {
+                       if (typeof account !== 'string' && !(account instanceof Account)) {
+                               throw new TypeError('Invalid account')
+                       }
+                       switch (this.subtype) {
+                               case 'change': {
+                                       this.link = Account.import(BURN_ADDRESS).publicKey
+                                       this.representative = (typeof account === 'string')
+                                               ? Account.import(account)
+                                               : account
+                                       break
+                               }
+                               case 'send': {
+                                       this.link = (typeof account === 'string')
+                                               ? Account.import(account).publicKey
+                                               : account.publicKey
+                                       this.representative = this.account.representative
+                                       break
+                               }
+                               default: {
+                                       throw new TypeError('Invalid subtype')
+                               }
+                       }
+                       return this
+               } catch (err) {
+                       throw new Error('Failed to change to, or send to, an Account', { cause: err })
+               }
+       }
+
        /**
        * Verifies the signature of the block. If a key is not provided, the public
        * key of the block's account will be used if it exists.