/
rho-json.ts
152 lines (135 loc) · 4.24 KB
/
rho-json.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* Converts `Par` object to JSON returned from the RNode gRPC API.
*
* Example of Rholang return values.
* ```scala
* new return(`rho:rchain:deployId`) in {
* return!("One argument") | // monadic
* return!((true, "A", "B")) | // monadic as tuple
* return!(true, "A", "B") | // polyadic
* return!(true) | return!("A") // multiple values
* }
* ```
* And the corresponding converted JSON objects.
*
* ```js
* [ 'One argument' ] // return!("One argument")
* [ [ true, 'A', 'B' ] ] // return!((true, "A", "B"))
* [ true, 'A', 'B' ] // return!(true, "A", "B")
* [ true, 'A' ] // return!(true) | return!("A")
* ```
*/
// TODO: make it stack safe
export const rhoParToJson = (input: Par): any => {
if (input === void 666 || input === null) return input
if (!!input.exprsList?.length) {
const vals = input.exprsList.map(rhoExprToJson)
return vals.length === 1 ? vals[0] : vals
} else if (!!input.unforgeablesList?.length) {
const vals = input.unforgeablesList.map(rhoUnforgeableToJson)
return vals.length === 1 ? vals[0] : vals
} else {
// For all other types of fields return undefined
// (not obvious representation in JSON)
return void 666
}
}
const rhoExprToJson = (x: Expr): any => {
// First check if complex fields can be deserialized
if (x.eListBody !== void 666) {
return x.eListBody.psList?.map(rhoParToJson)
} else if (x.eSetBody !== void 666) {
return x.eSetBody.psList?.map(rhoParToJson)
} else if (x.eTupleBody !== void 666) {
return x.eTupleBody.psList?.map(rhoParToJson)
} else if (x.eMapBody !== void 666) {
return x.eMapBody.kvsList?.reduce((acc, { key, value }) => {
// Get key from Par
const kRaw = rhoParToJson(key)
// Convert binary key to hex string
const k = kRaw instanceof Uint8Array ? uint8ArrayToHex(kRaw) : kRaw
// Get value from Par
const v = rhoParToJson(value)
// Skip key if undefined (not obvious representation in JSON)
return k === void 666 || k === null ? acc : { ...acc, [k]: v }
}, {})
// Only non-default primitives can be deserialized
} else if (x.gBool !== void 666) {
return x.gBool
} else if (x.gInt !== void 666) {
return x.gInt
} else if (x.gString !== void 666) {
return x.gString
} else if (x.gUri !== void 666) {
return x.gUri
} else if (x.gByteArray !== void 666) {
return base64ToUint8Array(x.gByteArray! as any)
} else {
// For all other types of fields return undefined
// (not obvious representation in JSON)
return void 666
}
}
const rhoUnforgeableToJson = (x: GUnforgeable): any => {
if (x.gDeployIdBody !== void 666) {
return base64ToUint8Array(x.gDeployIdBody.sig! as any)
} else if (x.gDeployerIdBody !== void 666) {
return base64ToUint8Array(x.gDeployerIdBody.publickey! as any)
} else if (x.gPrivateBody !== void 666) {
return base64ToUint8Array(x.gPrivateBody.id! as any)
}
}
const uint8ArrayToHex = (x: Uint8Array) =>
Buffer.from(x).toString('hex')
const base64ToUint8Array = (x: string) =>
Uint8Array.from(Buffer.from(x, 'base64'))
/**
* Types copied from generated JS code from protobuf
* - depends on specific version of specific RNode version but not changed often
*/
export interface Par {
readonly exprsList?: Expr[]
readonly unforgeablesList?: GUnforgeable[]
}
export interface Expr {
readonly gBool?: boolean
readonly gInt?: number
readonly gString?: string
readonly gUri?: string
readonly gByteArray?: Uint8Array
// Complex types
readonly eListBody?: EList
readonly eTupleBody?: ETuple
readonly eSetBody?: ESet
readonly eMapBody?: EMap
}
export interface EList {
readonly psList?: Par[]
}
export interface ETuple {
readonly psList?: Par[]
}
export interface ESet {
readonly psList?: Par[]
}
export interface EMap {
readonly kvsList?: KeyValuePair[]
}
export interface KeyValuePair {
readonly key: Par
readonly value: Par
}
export interface GUnforgeable {
readonly gPrivateBody?: GPrivate
readonly gDeployIdBody?: GDeployId
readonly gDeployerIdBody?: GDeployerId
}
export interface GPrivate {
readonly id?: Uint8Array
}
export interface GDeployId {
readonly sig?: Uint8Array
}
export interface GDeployerId {
readonly publickey?: Uint8Array
}