Skip to content

Commit

Permalink
new annotate and annotateCall methods (#60)
Browse files Browse the repository at this point in the history
* add experimental annotate methods to TupleType

* add experimental annotateCall methods to Function
  • Loading branch information
esaulpaugh committed Jan 20, 2024
1 parent ddfb63b commit 5b60d48
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/main/java/com/esaulpaugh/headlong/abi/ABIType.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ static void insertFFPadding(int n, ByteBuffer dest) {
dest.put(CACHED_NEG1_PADDING, 0, n);
}

private static final int LABEL_LEN = 6;
static final int LABEL_LEN = 6;
static final int LABEL_PADDED_LEN = LABEL_LEN + 3;
static final int CHARS_PER_LINE = "\n".length() + LABEL_PADDED_LEN + (FastHex.CHARS_PER_BYTE * UNIT_LENGTH_BYTES);

Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/esaulpaugh/headlong/abi/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,21 @@ public static String formatCall(byte[] buffer, final int offset, final int lengt
.append(selectorHex)
);
}

/**
* Experimental. Annotates the given function call and returns an informational formatted String. This method is
* subject to change or removal in a future release.
*/
public String annotateCall(byte[] call) {
return annotateCall(decodeCall(call));
}

/**
* Experimental. Annotates the function call given the {@code args} and returns an informational formatted String. This
* method is subject to change or removal in a future release.
*
*/
public String annotateCall(Tuple args) {
return name + ":\n" + ABIType.pad(0, "ID") + Strings.encode(selector) + '\n' + inputTypes.annotate(args);
}
}
94 changes: 94 additions & 0 deletions src/main/java/com/esaulpaugh/headlong/abi/TupleType.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.esaulpaugh.headlong.abi;

import com.esaulpaugh.headlong.util.Strings;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -420,4 +422,96 @@ public static TupleType of(String... typeStrings) {
static TupleType empty(int flags) {
return flags == ABIType.FLAGS_NONE ? EMPTY : new TupleType(EMPTY_TUPLE_STRING, false, EMPTY_ARRAY, null, null, flags);
}

/**
* Experimental. Annotates the given ABI encoding and returns an informational formatted String. This
* method is subject to change or removal in a future release.
*/
public String annotate(byte[] abi) {
return annotate(decode(ByteBuffer.wrap(abi), newUnitBuffer()));
}

/**
* Experimental. Annotates the ABI encoding of the given {@link Tuple} and returns an informational formatted String.
* This method is subject to change or removal in a future release.
*/
public String annotate(Tuple tuple) {
if (tuple.elements.length == 0) {
return "";
}
int row = 0;
final StringBuilder sb = new StringBuilder();
int i = 0;
final int last = tuple.elements.length - 1;
int offset = firstOffset;
for (;; i++) {
final ABIType<Object> t = get(i);
if (!t.dynamic) {
row = encodeTailAnnotated(row, i, t, tuple.elements[i], sb);
if (i >= last) {
break;
}
} else {
final ByteBuffer dest = ByteBuffer.allocate(OFFSET_LENGTH_BYTES);
insertIntUnsigned(offset, dest); // insert offset
sb.append(label(row++)).append(Strings.encode(dest));
annotate(sb, i, t, " offset");
if (i >= last) {
break;
}
offset += t.dynamicByteLength(tuple.elements[i]); // calculate next offset
}
}
for (i = 0; i < tuple.elements.length; i++) {
final ABIType<Object> t = get(i);
if (t.dynamic) {
row = encodeTailAnnotated(row, i, t, tuple.elements[i], sb);
}
}
sb.setLength(sb.length() - 1); // remove trailing newline
return sb.toString();
}

private static int encodeTailAnnotated(int row, int idx, ABIType<Object> t, Object v, StringBuilder sb) {
final ByteBuffer dest = ByteBuffer.allocate(t.validate(v));
t.encodeTail(v, dest);
final int len = dest.position();
dest.flip();
int i = 0;
if (i < len) {
final byte[] rowData = newUnitBuffer();
final boolean dynamicArray = t.dynamic && t instanceof ArrayType && ((ArrayType<?, ?>) t).getLength() == ArrayType.DYNAMIC_LENGTH;
appendAnnotatedRow(row++, sb, dest, rowData, idx, t, dynamicArray ? " length" : "");
i += UNIT_LENGTH_BYTES;
if (i < len) {
appendAnnotatedRow(row++, sb, dest, rowData, idx, t, dynamicArray ? "" : null);
i += UNIT_LENGTH_BYTES;
while (i < len) {
appendAnnotatedRow(row++, sb, dest, rowData, idx, t, null);
i += UNIT_LENGTH_BYTES;
}
}
}
return row;
}

private static void appendAnnotatedRow(int row, StringBuilder sb, ByteBuffer bb, byte[] rowData, int idx, ABIType<Object> t, String note) {
bb.get(rowData);
sb.append(label(row)).append(Strings.encode(rowData));
annotate(sb, idx, t, note);
}

private static String label(int row) {
String unpadded = Integer.toHexString(row * UNIT_LENGTH_BYTES);
return ABIType.pad(ABIType.LABEL_LEN - unpadded.length(), unpadded);
}

private static void annotate(StringBuilder sb, int idx, ABIType<?> t, String note) {
sb.append("\t[").append(idx).append("]");
if (note == null) {
sb.append(" ...").append('\n');
return;
}
sb.append(' ').append(t.canonicalType).append(note).append('\n');
}
}
33 changes: 33 additions & 0 deletions src/test/java/com/esaulpaugh/headlong/abi/EncodeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -168,6 +169,38 @@ public void fuzzSignatures() throws InterruptedException, TimeoutException {

private static final String ABI = "a71100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000230c5748dd399896196f734a17ecf80b2178adc067b361d5ba994ae6400000000b95ffb49ee209c0d760c3350ecaae6d4ad4ff7b62af4d91ef3b5ae4e00000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001816e178e3509b80a6a66a8827c8d5e62be37692efe0e22e59f936b8b000000000000000000000000000000000000000000000000000000000000000000000001a48b4c097589c15784ef0b97c17aa6dbb752f61fd1a272618d87a535000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033b6ae013f517ec3c2082aa77400d768168266af2c3c3680404075f300000000093ab24d3e36bb7194c68063c595ead7554b2e8ce3fa1e6498c0c1208000000009fd62e78a55909d9e882d50a8074384af470c9c13e618ccc761f09a700000000000000000000000000000000000000000000000000000000000000000000000182a731ded7c7b3dd45c528564df732e138a71dbccd14954c45b5c14e000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010daf12dcf419efe89f949a26f943bd7236aafbb8d6c4749fd27ed36f000000000000000000000000000000000000000000000000000000000000000000000003e6501ff6bc4074725885f5e65cc263b9ab0f098c14bc7cce3c2e4d2900000000f351c27189ce03746ce7dab3c560fb2bb430551b55a77a18380f066100000000e9753f8a5bb5e0da442d7e11d38d1eaafe5b6df463fd2256be199e380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000d05d841062e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000001b052db5529dc0433400000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013160000000000000000000000000000000000000000000000000000000000000001000000000051299437b715c73400c5e6e655c925eb10ee34669cf8f1ccf8febd";

@Test
public void testFunctionAnnotate() {
final Function f = Function.parse("do_something(bool,(),string,(int8,uint8),address,(uint16,bytes))");
final Tuple args = Tuple.of(
true,
Tuple.EMPTY,
"libertad..........................................................",
Tuple.of(-128, 255),
Address.wrap(Address.toChecksumAddress(BigInteger.TEN.shiftLeft(156))),
Tuple.of(65535, "carajo]0]0]0]0]0]0]0]0]0]0]0]0]0".getBytes(StandardCharsets.US_ASCII))
);
assertEquals(
"do_something:\n" +
"ID d88de50f\n" +
" 0 0000000000000000000000000000000000000000000000000000000000000001\t[0] bool\n" +
" 20 00000000000000000000000000000000000000000000000000000000000000c0\t[2] string offset\n" +
" 40 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80\t[3] (int8,uint8)\n" +
" 60 00000000000000000000000000000000000000000000000000000000000000ff\t[3] ...\n" +
" 80 000000000000000000000000a000000000000000000000000000000000000000\t[4] address\n" +
" a0 0000000000000000000000000000000000000000000000000000000000000140\t[5] (uint16,bytes) offset\n" +
" c0 0000000000000000000000000000000000000000000000000000000000000042\t[2] string length\n" +
" e0 6c696265727461642e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e\t[2] string\n" +
" 100 2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e\t[2] ...\n" +
" 120 2e2e000000000000000000000000000000000000000000000000000000000000\t[2] ...\n" +
" 140 000000000000000000000000000000000000000000000000000000000000ffff\t[5] (uint16,bytes)\n" +
" 160 0000000000000000000000000000000000000000000000000000000000000040\t[5] ...\n" +
" 180 0000000000000000000000000000000000000000000000000000000000000020\t[5] ...\n" +
" 1a0 636172616a6f5d305d305d305d305d305d305d305d305d305d305d305d305d30\t[5] ...",
f.annotateCall(f.encodeCall(args).array())
);
}

@Test
public void testFunctionFormat() throws Throwable {
testFormat(true, Function::formatCall);
Expand Down

0 comments on commit 5b60d48

Please sign in to comment.