11import { useDropzone } from "react-dropzone" ;
2- import { ProgramUploadFileInput , ProgramUploadFileOutput } from "./types" ;
3- import { mapUploadFileInputToOutput } from "./utils" ;
4- import { bytes , pvm } from "@typeberry/lib" ;
5- import { ExpectedState , MemoryChunkItem , PageMapItem , RegistersArray } from "@/types/pvm.ts" ;
6- import { SafeParseReturnType , z } from "zod" ;
2+ import { ProgramUploadFileOutput } from "./types" ;
3+ import { bytes } from "@typeberry/lib" ;
74import { useAppSelector } from "@/store/hooks" ;
85import { selectInitialState } from "@/store/debugger/debuggerSlice" ;
9- import { cn , getAsChunks , getAsPageMap } from "@/lib/utils.ts" ;
6+ import { cn } from "@/lib/utils.ts" ;
107import { UploadCloud } from "lucide-react" ;
118import { Button } from "../ui/button" ;
129import { useCallback , useEffect , useMemo , useState } from "react" ;
1310import { Input } from "../ui/input" ;
14- import { decodeSpiWithMetadata } from "@/utils/spi " ;
11+ import { loadFileFromUint8Array } from "./loadingUtils " ;
1512
1613type ProgramFileUploadProps = {
1714 onFileUpload : ( val : ProgramUploadFileOutput ) => void ;
@@ -20,148 +17,6 @@ type ProgramFileUploadProps = {
2017 close ?: ( ) => void ;
2118} ;
2219
23- type RawProgramUploadFileInput = unknown ;
24- type ValidationResult = SafeParseReturnType < RawProgramUploadFileInput , ProgramUploadFileInput > ;
25-
26- const validateJsonTestCaseSchema = ( json : RawProgramUploadFileInput ) : ValidationResult => {
27- const pageMapSchema = z . object ( {
28- address : z . number ( ) ,
29- length : z . number ( ) ,
30- "is-writable" : z . boolean ( ) ,
31- } ) ;
32-
33- const memorySchema = z . object ( {
34- address : z . number ( ) ,
35- contents : z . array ( z . number ( ) ) ,
36- } ) ;
37-
38- const schema = z . object ( {
39- name : z . string ( ) ,
40- "initial-regs" : z . array ( z . number ( ) ) . length ( 13 ) ,
41- "initial-pc" : z . number ( ) ,
42- "initial-page-map" : z . array ( pageMapSchema ) ,
43- "initial-memory" : z . array ( memorySchema ) ,
44- "initial-gas" : z . number ( ) ,
45- program : z . array ( z . number ( ) ) ,
46- "expected-status" : z . string ( ) ,
47- "expected-regs" : z . array ( z . number ( ) ) ,
48- "expected-pc" : z . number ( ) ,
49- "expected-memory" : z . array ( memorySchema ) ,
50- "expected-gas" : z . number ( ) ,
51- } ) ;
52-
53- return schema . safeParse ( json ) ;
54- } ;
55-
56- const generateErrorMessageFromZodValidation = ( result : ValidationResult ) : string => {
57- if ( ! result . error ) return "Unknown error occurred" ;
58-
59- const formattedErrors = result . error . errors . map ( ( err ) => {
60- const path = err . path . join ( " > " ) || "root" ;
61- return `Field: "${ path } " - ${ err . message } ` ;
62- } ) ;
63-
64- return `File validation failed with the following errors:\n\n${ formattedErrors . join ( "\n" ) } ` ;
65- } ;
66-
67- function loadFileFromUint8Array (
68- loadedFileName : string ,
69- uint8Array : Uint8Array ,
70- spiArgsAsBytes : Uint8Array ,
71- setError : ( e ?: string ) => void ,
72- onFileUpload : ( d : ProgramUploadFileOutput ) => void ,
73- initialState : ExpectedState ,
74- ) {
75- // reset error state
76- setError ( undefined ) ;
77-
78- // Try to parse file as a JSON first
79- let jsonFile = null ;
80- let stringContent = "" ;
81- let rawBytes = uint8Array ;
82-
83- try {
84- stringContent = new TextDecoder ( "utf-8" ) . decode ( uint8Array ) ;
85- } catch ( e ) {
86- console . warn ( "not a string" , e ) ;
87- }
88-
89- if ( stringContent . startsWith ( "0x" ) ) {
90- try {
91- rawBytes = bytes . BytesBlob . parseBlob ( stringContent ) . raw ;
92- } catch ( e ) {
93- console . warn ( "not a bytes blob" , e ) ;
94- }
95- } else {
96- try {
97- jsonFile = JSON . parse ( stringContent ) ;
98- } catch ( e ) {
99- console . warn ( "not a JSON" , e ) ;
100- }
101- }
102-
103- if ( jsonFile !== null ) {
104- const result = validateJsonTestCaseSchema ( jsonFile ) ;
105- if ( ! result . success ) {
106- console . warn ( "not a valid JSON" , result . error ) ;
107- const errorMessage = generateErrorMessageFromZodValidation ( result ) ;
108- setError ( errorMessage || "" ) ;
109- return ;
110- }
111-
112- onFileUpload ( mapUploadFileInputToOutput ( jsonFile , "JSON test" ) ) ;
113- return ;
114- }
115-
116- // Try to decode the program as an SPI
117- let spi = null ;
118- try {
119- spi = decodeSpiWithMetadata ( rawBytes , spiArgsAsBytes ) ;
120- } catch ( e ) {
121- console . warn ( "not an SPI blob" , e ) ;
122- }
123- if ( spi !== null ) {
124- const { code, memory, registers } = spi ;
125- const pageMap : PageMapItem [ ] = getAsPageMap ( memory ) ;
126- const chunks : MemoryChunkItem [ ] = getAsChunks ( memory ) ;
127-
128- onFileUpload ( {
129- program : Array . from ( code ) ,
130- name : `${ loadedFileName } [spi]` ,
131- isSpi : true ,
132- kind : "JAM SPI" ,
133- initial : {
134- regs : Array . from ( registers ) . map ( ( x ) => BigInt ( x as number | bigint ) ) as RegistersArray ,
135- pc : 0 ,
136- pageMap,
137- memory : chunks ,
138- gas : 10000n ,
139- } ,
140- } ) ;
141- return ;
142- }
143-
144- // Finally try to load program as a Generic
145- let program = null ;
146- try {
147- program = new pvm . ProgramDecoder ( rawBytes ) ;
148- } catch ( e ) {
149- console . warn ( "not a generic PVM" , e ) ;
150- }
151-
152- if ( program !== null ) {
153- onFileUpload ( {
154- program : Array . from ( rawBytes ) ,
155- name : `${ loadedFileName } [generic]` ,
156- isSpi : false ,
157- kind : "Generic PVM" ,
158- initial : initialState ,
159- } ) ;
160- } else {
161- setError ( "Unrecognized program format (see console)." ) ;
162- }
163- }
164-
16520export const ProgramFileUpload : React . FC < ProgramFileUploadProps > = ( { isError, onFileUpload, close, setError } ) => {
16621 const initialState = useAppSelector ( selectInitialState ) ;
16722 const spiArgs = useAppSelector ( ( state ) => state . debugger . spiArgs ) ;
0 commit comments