Skip to content

Commit

Permalink
ifl-744 add test/updates for calculating raw transaction size
Browse files Browse the repository at this point in the history
  • Loading branch information
jowparks committed Apr 26, 2023
1 parent 4b9dff1 commit 53b3fe2
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 19 deletions.
6 changes: 0 additions & 6 deletions ironfish-rust-nodejs/src/structs/note_encrypted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ pub const ENCRYPTED_NOTE_PLAINTEXT_LENGTH: u32 = ENCRYPTED_NOTE_SIZE as u32 + MA
#[napi]
pub const ENCRYPTED_NOTE_LENGTH: u32 =
NOTE_ENCRYPTION_KEY_LENGTH + ENCRYPTED_NOTE_PLAINTEXT_LENGTH + 96;
// 32 value commitment
//+ 32 note commitment
//+ 32 ephemeral public key
//+ 120 encrypted note
//+ 80 note encryption keys
//= 296 bytes

#[napi(js_name = "NoteEncrypted")]
pub struct NativeNoteEncrypted {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,44 @@
"publicAddress": "c96cc22b0a0e958c055eb6b357418e4a779b90f85750dfc9e618b36688eab944",
"createdAt": null
}
],
"RawTransaction should equal": [
{
"version": 2,
"id": "fb52a88e-3e8c-466f-bd34-c4dd7d75532d",
"name": "test",
"spendingKey": "4c3d51b6596832c4dc539aa5d6ecd036d338fbc21b6a64d26a7ccc838a87a000",
"viewKey": "a33795eafe7851f52fc4bfbe2a2ff60ad7bb409d6cc4bc19996a4d941a4f8cc526aee6da70f63bcac90ec4bd678b16bbe64299bed09da120bf28e321d3aab1aa",
"incomingViewKey": "cee508b90f3963e4099798a355ed0da0e123b193ff0f7cd6b7b4596caadf1e02",
"outgoingViewKey": "79f6d37e22fd3d9ca88b0d26a1dedebb4ef7e89fa31ac7632c1ee06a3f30628a",
"publicAddress": "6b971b1e7217ff4fe81c457c65d85ce8d7723fa52061a2194041efb556a99c19",
"createdAt": null
},
{
"header": {
"sequence": 2,
"previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA",
"noteCommitment": {
"type": "Buffer",
"data": "base64:9DdIBOwIdxNRoPgyIcDSxyCQwTV2oALwWD2w/K2dPEM="
},
"transactionCommitment": {
"type": "Buffer",
"data": "base64:v2mDXwx+HgVnlnZwDaTJfEzBSmmEHpHcvjPYjXbGJOg="
},
"target": "883423532389192164791648750371459257913741948437809479060803100646309888",
"randomness": "0",
"timestamp": 1682541250417,
"graffiti": "0000000000000000000000000000000000000000000000000000000000000000",
"noteSize": 4,
"work": "0"
},
"transactions": [
{
"type": "Buffer",
"data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAqZz03fllUAxM/C47JUjjmcVrt8H1IKtQY662BbzJsHOBBsM9VGn0kEUU/Da9UFfHfzW+n3Zud2xziSEG76e0SpoXl66oysLTkLvWcTycsumpWYVw5JNcPU7SBt2HACwfKN4awPDtSihU8dsjIbE+NBdMfLF6KMdBKjTC5fykZl0AjgmGflqizCNi3uucrM2Nm3HYjXIJvdv/R3AXA4TZD8BhduPvJEagPzLOcFxDwWuKLBAyjSrWU5SOmwrcqvJtvtJCwbIFNiUOIr5CO09PL+5ko/oVP4eZ2EHYNoKZRX0T8iZvPxVLuVIDlocEOn8zt2th/D2Erz1YHVbMw6B2VVoe9pmeJqQwwmI8yg1doZxhvPw8I7YjBPzwSGNQQp9x9vuW/n4O+bcopYA56O+eepc1r8CWU7+qmP9WjBCCskhK745L4B7hI3MTNwAyJ96m4S+PkHydl7m6QIZYIUGCLuYZSXKMd+QFXQJwfiyE8P+0I+gTtjaglmboCIZNCTyLbI4re2Xa2SXzqQk9agNVcyKHqG1j0D4qMVDBMCuvuhVCyFjBIy2yzQ9MCbV4mkE9AEsDjBdn4ehLB6Pi82M43awCFE0SblLFcEjryLy3OoZ3pK3IBVhliUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwxGhqNFhJQIu38oTMYoOTmInFtc4F/l21An0Z112s1dWXZ6OIGS8Xsdi8KJwm8vzAtBo7osJ/1NFTnjYT9uznAw=="
}
]
}
]
}
1 change: 1 addition & 0 deletions ironfish/src/primitives/noteEncrypted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import bufio from 'bufio'
import { Serde } from '../serde'
import { Note } from './note'

//192 + 328 = 520
export const NOTE_ENCRYPTED_SERIALIZED_SIZE_IN_BYTE = PROOF_LENGTH + ENCRYPTED_NOTE_LENGTH

export type NoteEncryptedHash = Buffer
Expand Down
4 changes: 4 additions & 0 deletions ironfish/src/primitives/rawTransaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ describe('RawTransaction', () => {
const mintedValue = valuesByAsset.get(asset.id())
Assert.isNotUndefined(mintedValue)
expect(mintedValue).toEqual(1n)

// should have same size for posted transaction and estimated size from raw transaction
const serializedPost = posted.serialize()
expect(raw.size(account.publicAddress)).toEqual(Buffer.byteLength(serializedPost))
})

it('should throw an error if the max mint value is exceeded', async () => {
Expand Down
53 changes: 47 additions & 6 deletions ironfish/src/primitives/rawTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
import {
AMOUNT_VALUE_LENGTH,
ASSET_LENGTH,
PROOF_LENGTH,
Transaction as NativeTransaction,
TRANSACTION_EXPIRATION_LENGTH,
TRANSACTION_FEE_LENGTH,
TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH,
TRANSACTION_SIGNATURE_LENGTH,
} from '@ironfish/rust-nodejs'
import { Asset, ASSET_ID_LENGTH } from '@ironfish/rust-nodejs'
import { BufferMap } from 'buffer-map'
import bufio from 'bufio'
import { Witness } from '../merkletree'
import { NoteHasher } from '../merkletree/hasher'
Expand Down Expand Up @@ -53,17 +57,54 @@ export class RawTransaction {
>
}[] = []

size(): number {
size(senderPublicAddress: string): number {
let size = 0
size += 1 // version
size += 8 // spends length
size += 8 // notes length
size += 8 // fee
size += 4 // expiration
size += 64 // signature
size += 8 // mints length
size += 8 // burns length
size += TRANSACTION_FEE_LENGTH // fee
size += TRANSACTION_EXPIRATION_LENGTH // expiration
size += TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH // public key randomness
size += this.spends.length * SPEND_SERIALIZED_SIZE_IN_BYTE
size += this.outputs.length * NOTE_ENCRYPTED_SERIALIZED_SIZE_IN_BYTE
size += this.mints.length * (ASSET_LENGTH + 8)
size +=
this.mints.length *
(PROOF_LENGTH + ASSET_LENGTH + AMOUNT_VALUE_LENGTH + TRANSACTION_SIGNATURE_LENGTH)
size += this.burns.length * (ASSET_ID_LENGTH + 8)
size += this.spends.length * SPEND_SERIALIZED_SIZE_IN_BYTE
size += TRANSACTION_SIGNATURE_LENGTH // signature

// Each asset might have a change note, which would need to be accounted for
const assetTotals = new BufferMap<bigint>()
for (const mint of this.mints) {
const asset = new Asset(senderPublicAddress, mint.name, mint.metadata)
const assetValue = assetTotals.get(asset.id())
assetTotals.set(asset.id(), assetValue ? assetValue + mint.value : mint.value)
}
for (const burn of this.burns) {
const assetValue = assetTotals.get(burn.assetId)
assetTotals.set(burn.assetId, assetValue ? assetValue - burn.value : -burn.value)
}
for (const spend of this.spends) {
const assetValue = assetTotals.get(spend.note.assetId())
assetTotals.set(
spend.note.assetId(),
assetValue ? assetValue - spend.note.value() : -spend.note.value(),
)
}
for (const output of this.outputs) {
const assetValue = assetTotals.get(output.note.assetId())
assetTotals.set(
output.note.assetId(),
assetValue ? assetValue + output.note.value() : output.note.value(),
)
}
for (const [, value] of assetTotals) {
if (value !== 0n) {
size += NOTE_ENCRYPTED_SERIALIZED_SIZE_IN_BYTE
}
}
return size
}

Expand Down
2 changes: 1 addition & 1 deletion ironfish/src/primitives/spend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Nullifier } from './nullifier'

export const SPEND_SERIALIZED_SIZE_IN_BYTE = 388
export const SPEND_SERIALIZED_SIZE_IN_BYTE = 356

export interface Spend {
nullifier: Nullifier
Expand Down
6 changes: 3 additions & 3 deletions ironfish/src/primitives/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class Transaction {

// spend description
this.spends = Array.from({ length: _spendsLength }, () => {
// proof
// proof 192
reader.seek(PROOF_LENGTH)
// value commitment
reader.seek(32)
Expand All @@ -68,10 +68,10 @@ export class Transaction {
const treeSize = reader.readU32() // 4
const nullifier = reader.readHash() // 32

// signature
// signature 64
reader.seek(TRANSACTION_SIGNATURE_LENGTH)

// total serialized size: 192 + 32 + 32 + 32 + 4 + 32 + 64 = 388 bytes
// total serialized size: 192 + 32 + 32 + 4 + 32 + 64 = 388 bytes
return {
size: treeSize,
commitment: rootHash,
Expand Down
4 changes: 3 additions & 1 deletion ironfish/src/rpc/routes/wallet/createTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type CreateTransactionRequest = {

export type CreateTransactionResponse = {
transaction: string
sizeInBytes: number
}

export const CreateTransactionRequestSchema: yup.ObjectSchema<CreateTransactionRequest> = yup
Expand Down Expand Up @@ -91,6 +92,7 @@ export const CreateTransactionRequestSchema: yup.ObjectSchema<CreateTransactionR
export const CreateTransactionResponseSchema: yup.ObjectSchema<CreateTransactionResponse> = yup
.object({
transaction: yup.string().defined(),
sizeInBytes: yup.number().defined(),
})
.defined()

Expand Down Expand Up @@ -183,9 +185,9 @@ router.register<typeof CreateTransactionRequestSchema, CreateTransactionResponse
try {
const transaction = await node.wallet.createTransaction(params)
const serialized = RawTransactionSerde.serialize(transaction)

request.end({
transaction: serialized.toString('hex'),
sizeInBytes: transaction.size(account.publicAddress),
})
} catch (e) {
if (e instanceof NotEnoughFundsError) {
Expand Down
4 changes: 2 additions & 2 deletions ironfish/src/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ export class Wallet {
}

if (options.feeRate) {
raw.fee = getFee(options.feeRate, raw.size())
raw.fee = getFee(options.feeRate, raw.size(options.account.publicAddress))
}

await this.fund(raw, {
Expand All @@ -928,7 +928,7 @@ export class Wallet {
})

if (options.feeRate) {
raw.fee = getFee(options.feeRate, raw.size())
raw.fee = getFee(options.feeRate, raw.size(options.account.publicAddress))
raw.spends = []

await this.fund(raw, {
Expand Down

0 comments on commit 53b3fe2

Please sign in to comment.