-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathRedeemers.ts
140 lines (123 loc) · 4.89 KB
/
Redeemers.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
import { Cardano, inConwayEra } from '../../..';
import { CborReader, CborReaderState, CborWriter } from '../../CBOR';
import { ExUnits } from '../../Common';
import { HexBlob, InvalidArgumentError } from '@cardano-sdk/util';
import { PlutusData } from '../../PlutusData';
import { Redeemer } from './Redeemer';
import { RedeemerTag } from './RedeemerTag';
import { hexToBytes } from '../../../util/misc';
const MAP_INDEX_EMBEDDED_GROUP_SIZE = 2;
const MAP_VALUE_EMBEDDED_GROUP_SIZE = 2;
export class Redeemers {
#values: Redeemer[];
private constructor(redeemers: Redeemer[]) {
this.#values = [...redeemers];
}
/**
* Serializes Redeemers into CBOR format.
* Redeemers are encoded as array when {@link inConwayEra} is false, and as map when
* {@link inConwayEra} is true.
*
* @returns The Redeemers in CBOR format.
*/
toCbor(): HexBlob {
const writer = new CborWriter();
// Encoding `redeemers` as `Map`:
// https://github.com/IntersectMBO/cardano-ledger/blob/master/eras/conway/impl/cddl-files/conway.cddl#L480
if (inConwayEra) {
// { + [ tag: redeemer_tag, index: uint ] => [ data: plutus_data, ex_units: ex_units ] }
const redeemersMap = new Map(this.#values.map((redeemer) => [`${redeemer.tag()}:${redeemer.index()}`, redeemer]));
writer.writeStartMap(redeemersMap.size);
for (const redeemer of redeemersMap.values()) {
// Map key cbor
writer.writeStartArray(2);
writer.writeInt(redeemer.tag());
writer.writeInt(redeemer.index());
// Map value cbor
writer.writeStartArray(2);
writer.writeEncodedValue(hexToBytes(redeemer.data().toCbor()));
writer.writeEncodedValue(hexToBytes(redeemer.exUnits().toCbor()));
}
} else {
// [ + [ tag: redeemer_tag, index: uint, data: plutus_data, ex_units: ex_units ] ]
writer.writeStartArray(this.#values.length);
for (const data of this.#values) {
writer.writeEncodedValue(Buffer.from(data.toCbor(), 'hex'));
}
}
return writer.encodeAsHex();
}
/**
* Deserializes the Redeemers from a CBOR byte array.
*
* @param cbor The CBOR encoded Redeemers array or map.
* @returns The new Redeemers instance.
*/
static fromCbor(cbor: HexBlob): Redeemers {
const redeemers: Redeemer[] = [];
const reader = new CborReader(cbor);
if (reader.peekState() === CborReaderState.StartMap) {
// { + [ tag: redeemer_tag, index: uint ] => [ data: plutus_data, ex_units: ex_units ] }
reader.readStartMap();
while (reader.peekState() !== CborReaderState.EndMap) {
// Read key cbor
const indexLength = reader.readStartArray();
if (indexLength !== MAP_INDEX_EMBEDDED_GROUP_SIZE)
throw new InvalidArgumentError(
'cbor',
`Redeemers map index should be an array of ${MAP_INDEX_EMBEDDED_GROUP_SIZE} elements, but got an array of ${indexLength} elements`
);
const tag: RedeemerTag = Number(reader.readUInt()) as RedeemerTag;
const index = reader.readUInt();
reader.readEndArray();
// Read value cbor
const valueLength = reader.readStartArray();
if (valueLength !== MAP_VALUE_EMBEDDED_GROUP_SIZE)
throw new InvalidArgumentError(
'cbor',
`Redeemers map value should be an array of ${MAP_VALUE_EMBEDDED_GROUP_SIZE} elements, but got an array of ${valueLength} elements`
);
const data = PlutusData.fromCbor(HexBlob.fromBytes(reader.readEncodedValue()));
const exUnits = ExUnits.fromCbor(HexBlob.fromBytes(reader.readEncodedValue()));
reader.readEndArray();
redeemers.push(new Redeemer(tag, index, data, exUnits));
}
reader.readEndMap();
} else {
// [ + [ tag: redeemer_tag, index: uint, data: plutus_data, ex_units: ex_units ] ]
reader.readStartArray();
while (reader.peekState() !== CborReaderState.EndArray) {
redeemers.push(Redeemer.fromCbor(HexBlob.fromBytes(reader.readEncodedValue())));
}
reader.readEndArray();
}
return new Redeemers(redeemers);
}
/**
* Creates a Core Redeemers array from the current Redeemer object.
*
* @returns The Core Redeemers array.
*/
toCore(): Cardano.Redeemer[] {
return this.#values.map((redeemer) => redeemer.toCore());
}
/**
* Creates a Redeemers object from the given Core Redeemers array.
*
* @param redeemers core Redeemer array.
*/
static fromCore(redeemers: Cardano.Redeemer[]): Redeemers {
return new Redeemers(redeemers.map((redeemer) => Redeemer.fromCore(redeemer)));
}
/** @returns a copy of the underlying {@link Redeemer}[] */
values(): readonly Redeemer[] {
return this.#values;
}
/** @param redeemers replace the existing redeemers with the ones provided here */
setValues(redeemers: Redeemer[]) {
this.#values = [...redeemers];
}
size() {
return this.#values.length;
}
}