Skip to content

Commit a30a3b7

Browse files
author
Liulei
committed
feat(tvm): add blob opcodes
add kzg point evaluation precompile contract
1 parent e649699 commit a30a3b7

17 files changed

Lines changed: 594 additions & 0 deletions

File tree

actuator/src/main/java/org/tron/core/vm/Op.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ public class Op {
120120
public static final int SELFBALANCE = 0x47;
121121
// (0x48) Get block's basefee
122122
public static final int BASEFEE = 0x48;
123+
// (0x49) Get blob hash
124+
public static final int BLOBAHASH = 0x49;
125+
// (0x4a) Get block's blob basefee
126+
public static final int BLOBBASEFEE = 0x4a;
123127

124128
/* Memory, Storage and Flow Operations */
125129
// (0x50) Remove item from stack

actuator/src/main/java/org/tron/core/vm/OperationActions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,16 @@ public static void mCopyAction(Program program) {
677677
program.step();
678678
}
679679

680+
public static void blobHashAction(Program program) {
681+
program.stackPush(DataWord.ZERO());
682+
program.step();
683+
}
684+
685+
public static void blobBaseFeeAction(Program program) {
686+
program.stackPush(DataWord.ZERO());
687+
program.step();
688+
}
689+
680690
public static void push0Action(Program program) {
681691
program.stackPush(DataWord.ZERO());
682692
program.step();

actuator/src/main/java/org/tron/core/vm/OperationRegistry.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,5 +681,17 @@ public static void appendCancunOperations(JumpTable table) {
681681
EnergyCost::getMCopyCost,
682682
OperationActions::mCopyAction,
683683
proposal));
684+
685+
table.set(new Operation(
686+
Op.BLOBAHASH, 1, 1,
687+
EnergyCost::getVeryLowTierCost,
688+
OperationActions::blobHashAction,
689+
proposal));
690+
691+
table.set(new Operation(
692+
Op.BLOBBASEFEE, 0, 1,
693+
EnergyCost::getBaseTierCost,
694+
OperationActions::blobBaseFeeAction,
695+
proposal));
684696
}
685697
}

actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
import java.util.concurrent.ExecutorService;
3333
import java.util.concurrent.Future;
3434
import java.util.concurrent.TimeUnit;
35+
import ethereum.ckzg4844.CKZG4844JNI;
3536
import lombok.AllArgsConstructor;
3637
import lombok.Getter;
3738
import lombok.Setter;
3839
import lombok.extern.slf4j.Slf4j;
3940
import org.apache.commons.lang3.ArrayUtils;
4041
import org.apache.commons.lang3.tuple.Pair;
4142
import org.apache.commons.lang3.tuple.Triple;
43+
import org.bouncycastle.util.encoders.Hex;
4244
import org.tron.common.crypto.Blake2bfMessageDigest;
4345
import org.tron.common.crypto.Hash;
4446
import org.tron.common.crypto.SignUtils;
@@ -104,6 +106,7 @@ public class PrecompiledContracts {
104106

105107
private static final EthRipemd160 ethRipemd160 = new EthRipemd160();
106108
private static final Blake2F blake2F = new Blake2F();
109+
private static final KZGPointEvaluation kzgPointEvaluation = new KZGPointEvaluation();
107110

108111
// FreezeV2 PrecompileContracts
109112
private static final GetChainParameter getChainParameter = new GetChainParameter();
@@ -198,6 +201,9 @@ public class PrecompiledContracts {
198201
private static final DataWord blake2FAddr = new DataWord(
199202
"0000000000000000000000000000000000000000000000000000000000020009");
200203

204+
private static final DataWord kzgPointEvaluationAddr = new DataWord(
205+
"000000000000000000000000000000000000000000000000000000000002000a");
206+
201207
public static PrecompiledContract getOptimizedContractForConstant(PrecompiledContract contract) {
202208
try {
203209
Constructor<?> constructor = contract.getClass().getDeclaredConstructor();
@@ -279,6 +285,9 @@ public static PrecompiledContract getContractForAddress(DataWord address) {
279285
if (VMConfig.allowTvmCompatibleEvm() && address.equals(blake2FAddr)) {
280286
return blake2F;
281287
}
288+
if (VMConfig.allowTvmCancun() && address.equals(kzgPointEvaluationAddr)) {
289+
return kzgPointEvaluation;
290+
}
282291

283292
if (VMConfig.allowTvmFreezeV2()) {
284293
if (address.equals(getChainParameterAddr)) {
@@ -2191,4 +2200,49 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
21912200
}
21922201
}
21932202

2203+
public static class KZGPointEvaluation extends PrecompiledContract {
2204+
2205+
private static final int BLOB_VERIFY_INPUT_LENGTH = 192;
2206+
private static final byte BLOB_COMMITMENT_VERSION_KZG = 0x01;
2207+
private static final byte[] BLOB_PRECOMPILED_RETURN_VALUE = Hex.decode(
2208+
"0000000000000000000000000000000000000000000000000000000000001000" +
2209+
"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
2210+
2211+
@Override
2212+
public long getEnergyForData(byte[] data) {
2213+
return 50000;
2214+
}
2215+
2216+
@Override
2217+
public Pair<Boolean, byte[]> execute(byte[] data) {
2218+
if (data == null || data.length != BLOB_VERIFY_INPUT_LENGTH) {
2219+
return Pair.of(false, DataWord.ZERO().getData());
2220+
}
2221+
2222+
byte[] versionedHash = parseBytes(data, 0, 32);
2223+
byte[] z = parseBytes(data, 32, 32);
2224+
byte[] y = parseBytes(data, 64, 32);
2225+
byte[] commitment = parseBytes(data, 96, 48);
2226+
byte[] proof = parseBytes(data, 144, 48);
2227+
2228+
byte[] hash = Sha256Hash.hash(
2229+
CommonParameter.getInstance().isECKeyCryptoEngine(), commitment);
2230+
hash[0] = BLOB_COMMITMENT_VERSION_KZG;
2231+
if (!Arrays.equals(versionedHash, hash)) {
2232+
return Pair.of(false, DataWord.ZERO().getData());
2233+
}
2234+
2235+
try {
2236+
if (CKZG4844JNI.verifyKzgProof(commitment, z, y, proof)) {
2237+
return Pair.of(true, BLOB_PRECOMPILED_RETURN_VALUE);
2238+
} else {
2239+
return Pair.of(false, DataWord.ZERO().getData());
2240+
}
2241+
} catch (RuntimeException exception) {
2242+
logger.warn("KZG point evaluation precompile contract failed", exception);
2243+
return Pair.of(false, DataWord.ZERO().getData());
2244+
}
2245+
}
2246+
}
2247+
21942248
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package ethereum.ckzg4844;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.UncheckedIOException;
6+
import java.math.BigInteger;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.StandardCopyOption;
10+
11+
public class CKZG4844JNI {
12+
13+
private static final String LIBRARY_NAME = "ckzg4844jni";
14+
private static final String PLATFORM_NATIVE_LIBRARY_NAME = System.mapLibraryName(LIBRARY_NAME);
15+
16+
/** Loads the appropriate native library based on your platform. */
17+
public static void loadNativeLibrary() {
18+
String libraryResourcePath =
19+
"/lib/" + System.getProperty("os.arch") + "/" + PLATFORM_NATIVE_LIBRARY_NAME;
20+
InputStream libraryResource = CKZG4844JNI.class.getResourceAsStream(libraryResourcePath);
21+
if (libraryResource == null) {
22+
try {
23+
System.loadLibrary(LIBRARY_NAME);
24+
} catch (UnsatisfiedLinkError __) {
25+
String exceptionMessage =
26+
String.format(
27+
"Couldn't load native library (%s). It wasn't available at %s or the library path.",
28+
LIBRARY_NAME, libraryResourcePath);
29+
throw new RuntimeException(exceptionMessage);
30+
}
31+
} else {
32+
try {
33+
Path tempDir = Files.createTempDirectory(LIBRARY_NAME + "@");
34+
tempDir.toFile().deleteOnExit();
35+
Path tempDll = tempDir.resolve(PLATFORM_NATIVE_LIBRARY_NAME);
36+
tempDll.toFile().deleteOnExit();
37+
Files.copy(libraryResource, tempDll, StandardCopyOption.REPLACE_EXISTING);
38+
libraryResource.close();
39+
System.load(tempDll.toString());
40+
} catch (IOException ex) {
41+
throw new UncheckedIOException(ex);
42+
}
43+
}
44+
}
45+
46+
/** Scalar field modulus of BLS12-381. */
47+
public static final BigInteger BLS_MODULUS =
48+
new BigInteger(
49+
"52435875175126190479447740508185965837690552500527637822603658699938581184513");
50+
51+
/** The number of bytes in a g1 point. */
52+
protected static final int BYTES_PER_G1 = 48;
53+
54+
/** The number of bytes in a g2 point. */
55+
protected static final int BYTES_PER_G2 = 96;
56+
57+
/** The number of bytes in a BLS scalar field element. */
58+
public static final int BYTES_PER_FIELD_ELEMENT = 32;
59+
60+
/** The number of bits in a BLS scalar field element. */
61+
protected static final int BITS_PER_FIELD_ELEMENT = 255;
62+
63+
/** The number of field elements in a blob. */
64+
public static final int FIELD_ELEMENTS_PER_BLOB = 4096;
65+
66+
/** The number of field elements in an extended blob. */
67+
protected static final int FIELD_ELEMENTS_PER_EXT_BLOB = FIELD_ELEMENTS_PER_BLOB * 2;
68+
69+
/** The number of field elements in a cell. */
70+
public static final int FIELD_ELEMENTS_PER_CELL = 64;
71+
72+
/** The number of bytes in a KZG commitment. */
73+
public static final int BYTES_PER_COMMITMENT = 48;
74+
75+
/** The number of bytes in a KZG proof. */
76+
public static final int BYTES_PER_PROOF = 48;
77+
78+
/** The number of bytes in a blob. */
79+
public static final int BYTES_PER_BLOB = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT;
80+
81+
/** The number of bytes in a single cell. */
82+
public static final int BYTES_PER_CELL = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL;
83+
84+
/** The number of cells in an extended blob. */
85+
public static final int CELLS_PER_EXT_BLOB =
86+
FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL;
87+
88+
private CKZG4844JNI() {}
89+
90+
/**
91+
* Loads the trusted setup from a file. Once loaded, the same setup will be used for all the
92+
* crypto native calls. To load a new setup, free the current one by calling {@link
93+
* #freeTrustedSetup()} and then load the new one. If no trusted setup has been loaded, all the
94+
* crypto native calls will throw a {@link RuntimeException}.
95+
*
96+
* @param file a path to a trusted setup file
97+
* @param precompute configurable value between 0-15
98+
* @throws CKZGException if there is a crypto error
99+
*/
100+
public static native void loadTrustedSetup(String file, long precompute);
101+
102+
/**
103+
* An alternative to {@link #loadTrustedSetup(String,long)}. Loads the trusted setup from method
104+
* parameters instead of a file.
105+
*
106+
* @param g1MonomialBytes g1 values in monomial form as bytes
107+
* @param g1LagrangeBytes g1 values in Lagrange form as bytes
108+
* @param g2MonomialBytes g2 values in monomial form as bytes
109+
* @param precompute configurable value between 0-15
110+
* @throws CKZGException if there is a crypto error
111+
*/
112+
public static native void loadTrustedSetup(
113+
byte[] g1MonomialBytes, byte[] g1LagrangeBytes, byte[] g2MonomialBytes, long precompute);
114+
115+
/**
116+
* An alternative to {@link #loadTrustedSetup(String,long)}. Loads the trusted setup from a
117+
* resource.
118+
*
119+
* @param clazz the class to use to get the resource
120+
* @param resource the resource name that contains the trusted setup
121+
* @param precompute configurable value between 0-15
122+
* @param <T> the type of the class
123+
* @throws CKZGException if there is a crypto error
124+
* @throws IllegalArgumentException if the resource does not exist
125+
*/
126+
public static <T> void loadTrustedSetupFromResource(
127+
String resource, Class<T> clazz, long precompute) {
128+
InputStream is = clazz.getResourceAsStream(resource);
129+
if (is == null) {
130+
throw new IllegalArgumentException("Resource " + resource + " does not exist.");
131+
}
132+
133+
try (InputStream closableIs = is) {
134+
Path jniWillLoadFrom = Files.createTempFile("kzg-trusted-setup", ".txt");
135+
jniWillLoadFrom.toFile().deleteOnExit();
136+
Files.copy(closableIs, jniWillLoadFrom, StandardCopyOption.REPLACE_EXISTING);
137+
loadTrustedSetup(jniWillLoadFrom.toString(), precompute);
138+
} catch (IOException ex) {
139+
throw new UncheckedIOException("Error loading trusted setup from resource " + resource, ex);
140+
}
141+
}
142+
143+
/**
144+
* Free the current trusted setup. This method will throw an exception if no trusted setup has
145+
* been loaded.
146+
*/
147+
public static native void freeTrustedSetup();
148+
149+
/**
150+
* Calculates commitment for a given blob
151+
*
152+
* @param blob blob bytes
153+
* @return the commitment
154+
* @throws CKZGException if there is a crypto error
155+
*/
156+
public static native byte[] blobToKzgCommitment(byte[] blob);
157+
158+
/**
159+
* Compute proof at point z for the polynomial represented by blob.
160+
*
161+
* @param blob blob bytes
162+
* @param zBytes a point
163+
* @return an instance of {@link ProofAndY} holding the proof and the value y = f(z)
164+
* @throws CKZGException if there is a crypto error
165+
*/
166+
public static native ProofAndY computeKzgProof(byte[] blob, byte[] zBytes);
167+
168+
/**
169+
* Given a blob, return the KZG proof that is used to verify it against the commitment
170+
*
171+
* @param blob blob bytes
172+
* @param commitmentBytes commitment bytes
173+
* @return the proof
174+
* @throws CKZGException if there is a crypto error
175+
*/
176+
public static native byte[] computeBlobKzgProof(byte[] blob, byte[] commitmentBytes);
177+
178+
/**
179+
* Verify the proof by point evaluation for the given commitment
180+
*
181+
* @param commitmentBytes commitment bytes
182+
* @param zBytes Z
183+
* @param yBytes Y
184+
* @param proofBytes the proof that needs verifying
185+
* @return true if the proof is valid and false otherwise
186+
* @throws CKZGException if there is a crypto error
187+
*/
188+
public static native boolean verifyKzgProof(
189+
byte[] commitmentBytes, byte[] zBytes, byte[] yBytes, byte[] proofBytes);
190+
191+
/**
192+
* Given a blob and a KZG proof, verify that the blob data corresponds to the provided commitment.
193+
*
194+
* @param blob blob bytes
195+
* @param commitmentBytes commitment bytes
196+
* @param proofBytes proof bytes
197+
* @return true if the proof is valid and false otherwise
198+
* @throws CKZGException if there is a crypto error
199+
*/
200+
public static native boolean verifyBlobKzgProof(
201+
byte[] blob, byte[] commitmentBytes, byte[] proofBytes);
202+
203+
/**
204+
* Given a list of blobs and blob KZG proofs, verify that they correspond to the provided
205+
* commitments.
206+
*
207+
* @param blobs flattened blobs bytes
208+
* @param commitmentsBytes flattened commitments bytes
209+
* @param proofsBytes flattened proofs bytes
210+
* @param count the number of blobs (should be same as the number of proofs and commitments)
211+
* @return true if the proof is valid and false otherwise
212+
* @throws CKZGException if there is a crypto error
213+
*/
214+
public static native boolean verifyBlobKzgProofBatch(
215+
byte[] blobs, byte[] commitmentsBytes, byte[] proofsBytes, long count);
216+
217+
/**
218+
* Get the cells for a given blob.
219+
*
220+
* @param blob the blob to get cells for
221+
* @return the cells
222+
* @throws CKZGException if there is a crypto error
223+
*/
224+
public static native byte[] computeCells(byte[] blob);
225+
226+
/**
227+
* Get the cells and proofs for a given blob.
228+
*
229+
* @param blob the blob to get cells/proofs for
230+
* @return a CellsAndProofs object
231+
* @throws CKZGException if there is a crypto error
232+
*/
233+
public static native CellsAndProofs computeCellsAndKzgProofs(byte[] blob);
234+
235+
/**
236+
* Given at least 50% of cells, reconstruct the missing cells/proofs.
237+
*
238+
* @param cellIndices the identifiers for the cells you have
239+
* @param cells the cells you have
240+
* @return all cells/proofs for that blob
241+
* @throws CKZGException if there is a crypto error
242+
*/
243+
public static native CellsAndProofs recoverCellsAndKzgProofs(long[] cellIndices, byte[] cells);
244+
245+
/**
246+
* Verify that multiple cells' proofs are valid.
247+
*
248+
* @param commitmentsBytes the commitments for each cell
249+
* @param cellIndices the column index for each cell
250+
* @param cells the cells to verify
251+
* @param proofsBytes the proof for each cell
252+
* @return true if the cells are valid with respect to the given commitments
253+
* @throws CKZGException if there is a crypto error
254+
*/
255+
public static native boolean verifyCellKzgProofBatch(
256+
byte[] commitmentsBytes, long[] cellIndices, byte[] cells, byte[] proofsBytes);
257+
}

0 commit comments

Comments
 (0)