@@ -3,6 +3,11 @@ import * as multihash from 'multiformats/hashes/digest'
33import { CID } from 'multiformats/cid'
44import { Keyring } from '@polkadot/keyring' ;
55import { getPolkadotSigner } from '@polkadot-api/signer' ;
6+ import * as dagPB from '@ipld/dag-pb'
7+ import { UnixFS } from 'ipfs-unixfs'
8+ import { createCanvas } from "canvas" ;
9+ import fs from "fs" ;
10+ import assert from "assert" ;
611
712export async function waitForNewBlock ( ) {
813 // TODO: wait for a new block.
@@ -64,3 +69,107 @@ export function setupKeyringAndSigners(sudoSeed, accountSeed) {
6469 whoAddress : whoAccount . address
6570 } ;
6671}
72+
73+ /**
74+ * Build a UnixFS DAG-PB file node from raw chunks.
75+ *
76+ * (By default with SHA2 multihash)
77+ */
78+ export async function buildUnixFSDagPB ( chunks , mhCode = 0x12 ) {
79+ if ( ! chunks ?. length ) {
80+ throw new Error ( 'β buildUnixFSDag: chunks[] is empty' )
81+ }
82+
83+ // UnixFS blockSizes = sizes of child blocks
84+ const blockSizes = chunks . map ( c => c . len )
85+
86+ console . log ( `π§© Building UnixFS DAG from chunks:
87+ β’ totalChunks: ${ chunks . length }
88+ β’ blockSizes: ${ blockSizes . join ( ', ' ) } ` )
89+
90+ // Build UnixFS file metadata (no inline data here)
91+ const fileData = new UnixFS ( {
92+ type : 'file' ,
93+ blockSizes
94+ } )
95+
96+ // DAG-PB node: our file with chunk links
97+ const dagNode = dagPB . prepare ( {
98+ Data : fileData . marshal ( ) ,
99+ Links : chunks . map ( c => ( {
100+ Name : '' ,
101+ Tsize : c . len ,
102+ Hash : c . cid
103+ } ) )
104+ } )
105+
106+ // Encode DAG-PB
107+ const dagBytes = dagPB . encode ( dagNode )
108+
109+ // Hash DAG to produce CIDv1
110+ const rootCid = await cidFromBytes ( dagBytes , dagPB . code , mhCode )
111+
112+ console . log ( `β
DAG root CID: ${ rootCid . toString ( ) } ` )
113+
114+ return { rootCid, dagBytes }
115+ }
116+
117+ /**
118+ * Generates (dynamic) images based on the input text.
119+ */
120+ export function generateTextImage ( file , text , width = 800 , height = 600 ) {
121+ const canvas = createCanvas ( width , height ) ;
122+ const ctx = canvas . getContext ( "2d" ) ;
123+
124+ // π¨ Background
125+ ctx . fillStyle = randomColor ( ) ;
126+ ctx . fillRect ( 0 , 0 , width , height ) ;
127+
128+ // π Random shapes
129+ for ( let i = 0 ; i < 15 ; i ++ ) {
130+ ctx . beginPath ( ) ;
131+ ctx . fillStyle = randomColor ( ) ;
132+ ctx . arc (
133+ Math . random ( ) * width ,
134+ Math . random ( ) * height ,
135+ Math . random ( ) * 120 ,
136+ 0 ,
137+ Math . PI * 2
138+ ) ;
139+ ctx . fill ( ) ;
140+ }
141+
142+ // βοΈ Draw your text
143+ ctx . font = "bold 40px Sans" ;
144+ ctx . fillStyle = "white" ;
145+ ctx . textAlign = "center" ;
146+ ctx . textBaseline = "middle" ;
147+
148+ // Add text with shadow for readability
149+ ctx . shadowColor = "black" ;
150+ ctx . shadowBlur = 8 ;
151+
152+ ctx . fillText ( text , width / 2 , height / 2 ) ;
153+
154+ let jpegBytes = canvas . toBuffer ( "image/jpeg" ) ;
155+ fs . writeFileSync ( file , jpegBytes ) ;
156+ console . log ( "Saved to file:" , file ) ;
157+ }
158+
159+ function randomColor ( ) {
160+ return `rgb(${ rand255 ( ) } , ${ rand255 ( ) } , ${ rand255 ( ) } )` ;
161+ }
162+
163+ function rand255 ( ) {
164+ return Math . floor ( Math . random ( ) * 256 ) ;
165+ }
166+
167+ export function filesAreEqual ( path1 , path2 ) {
168+ const data1 = fs . readFileSync ( path1 ) ;
169+ const data2 = fs . readFileSync ( path2 ) ;
170+ assert . deepStrictEqual ( data1 . length , data2 . length )
171+
172+ for ( let i = 0 ; i < data1 . length ; i ++ ) {
173+ assert . deepStrictEqual ( data1 [ i ] , data2 [ i ] )
174+ }
175+ }
0 commit comments