Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'java'

group = 'network.casper'
// Version number update for release
version='2.7.0-BETA.2'
version='2.7.0-BETA.3'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand All @@ -24,7 +24,6 @@ repositories {
}

dependencies {
implementation "dev.oak3:sbs4j:${sbs4jVersion}"
implementation "io.github.oak:jsonrpc4j:${jsonrpc4jVersion}"
implementation "org.bouncycastle:bcpkix-jdk15on:${bouncyCastleVersion}"
implementation "org.web3j:core:${web3jVersion}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package com.casper.sdk.model.transaction.field;

import com.casper.sdk.exception.NoSuchTypeException;
import com.casper.sdk.model.clvalue.serde.CasperSerializableObject;
import com.casper.sdk.model.clvalue.serde.Target;
import dev.oak3.sbs4j.DeserializerBuffer;
import dev.oak3.sbs4j.SerializerBuffer;
import dev.oak3.sbs4j.exception.ValueDeserializationException;
import dev.oak3.sbs4j.exception.ValueSerializationException;
import dev.oak3.sbs4j.interfaces.DeserializableObject;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

/**
* The call table serialization envelope builder used to serialize and deserialize transaction fields/
*
* @author ian@meywood.com
*/
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class CalltableSerializationEnvelopeBuilder implements CasperSerializableObject, DeserializableObject {

/** The fields to serialize */
private List<Field> fields = new ArrayList<>();
/** The current field index */
private long currentFieldIndex = -1;
/** The expected number of fields */
private long expectedFields;
/** The offset of the current field om the serialized bytes */
private long offset = 0;
/** The total size of all field values when serialized */
private long size = 0;

/**
* Add a field to the envelope.
*
* @param index the zero based index
* @param value the value to be serialized to value bytes of the field
*/
public <T> void addField(final int index, final T value) throws ValueSerializationException {
this.addField(new Field(index, this.offset, value));
}

/**
* Add a field to the envelope.
*
* @param index the zero based index
* @param value the bytes of the value to add
*/
public void addFieldBytes(final int index, final byte[] value) {

this.addField(new Field((short) index, offset, value));
}

/**
* Adds a field to the envelope.
*
* @param field the field to add
*/
public void addField(final Field field) {
if (this.currentFieldIndex >= field.getIndex()) {
throw new IllegalArgumentException("Field index must be greater than the previous field index");
}

if (this.currentFieldIndex + 1 != field.getIndex()) {
throw new IllegalArgumentException("Field index must be sequential");
}

this.fields.add(field);
this.currentFieldIndex = field.getIndex();
this.size += field.getValue().length;
this.offset += field.getValue().length;
}

/**
* Obtains the fields and converts its byte value to the specified type.
*
* @param index the index of the field
* @param clazz the tye to convert the field value to
* @param <T> the type to convert the field value to
* @return the field value as the specified type
* @throws ValueDeserializationException if the field value cannot be converted to the specified type
*/
public <T> T getFieldValue(final int index, final Class<T> clazz) throws ValueDeserializationException {
final Field field = this.fields.get(index);
return field.getValue(clazz);
}

/**
* Obtains the field's value bytes.
*
* @param index the index of the field
* @return the fields value bytes
*/
public byte[] getFieldBytes(final int index) {
return fields.get(index).getValue();
}

@Override
public void serialize(final SerializerBuffer ser, final Target target) throws ValueSerializationException, NoSuchTypeException {

if (this.fields.size() != this.expectedFields) {
throw new IllegalArgumentException("Field index must be expected length " + this.expectedFields);
}

// Write the number of fields
ser.writeI32(this.fields.size());

for (final Field field : this.fields) {
field.serialize(ser, target);
}

// Write total bytes of all field values
ser.writeU32(this.size);
for (final Field field : this.fields) {
ser.writeByteArray(field.getValue());
}
}

@Override
public void deserialize(final DeserializerBuffer deserializerBuffer) throws ValueDeserializationException {

final long numFields = deserializerBuffer.readU32();

for (int i = 0; i < numFields; i++) {
final Field field = new Field();
field.deserialize(deserializerBuffer);
this.fields.add(field);
}

this.size = deserializerBuffer.readU32();
for (Field field : this.fields) {
final long fieldValueLen = getFieldLength(field);
field.setValue(deserializerBuffer.readByteArray((int) fieldValueLen));
}
}

private long getFieldLength(final Field field) {
if (field.getIndex() == this.fields.size() - 1) {
return size - field.getOffset();
} else {
return this.fields.get(field.getIndex() + 1).getOffset() - field.getOffset();
}
}
}
122 changes: 122 additions & 0 deletions src/main/java/com/casper/sdk/model/transaction/field/Field.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.casper.sdk.model.transaction.field;

import com.casper.sdk.exception.NoSuchTypeException;
import com.casper.sdk.model.clvalue.serde.CasperSerializableObject;
import com.casper.sdk.model.clvalue.serde.Target;
import dev.oak3.sbs4j.DeserializerBuffer;
import dev.oak3.sbs4j.SerializerBuffer;
import dev.oak3.sbs4j.exception.ValueDeserializationException;
import dev.oak3.sbs4j.exception.ValueSerializationException;
import dev.oak3.sbs4j.interfaces.DeserializableObject;
import dev.oak3.sbs4j.interfaces.SerializableObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* An indexed field with an offset and value.
*
* @author ian@meywood.com
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Field implements CasperSerializableObject, DeserializableObject {

/** The field index */
private short index;
/** The offset of the field's value when written to bytes */
private long offset;
/** The field value as bytes */
private byte[] value;

/**
* Constructs a field with the specified index, offset and value.
*
* @param index the index of the field
* @param offset the offset of the fields bytes within a CalltableSerializationEnvelope
* @param value the value of the field
* @throws ValueSerializationException if the value cannot be serialized
*/
public Field(final int index, final long offset, final Object value) throws ValueSerializationException {
final SerializerBuffer serializerBuffer = new SerializerBuffer();

if (value instanceof SerializableObject) {
((SerializableObject) value).serialize(serializerBuffer);
} else if (value instanceof byte[]) {
serializerBuffer.writeByteArray((byte[]) value);
} else if (value.getClass().equals(Byte.class)) {
serializerBuffer.writeU8((Byte) value);
} else if (value.getClass().equals(Long.class)) {
serializerBuffer.writeI64((Long) value);
} else if (value.getClass().equals(Integer.class)) {
serializerBuffer.writeI32((Integer) value);
} else if (value.getClass().equals(Short.class)) {
serializerBuffer.writeU16((Short) value);
} else if (value.getClass().equals(String.class)) {
serializerBuffer.writeString((String) value);
} else {
throw new ValueSerializationException("Unsupported type " + value.getClass().getName());
}

this.index = (short) index;
this.offset = offset;
this.value = serializerBuffer.toByteArray();
}

@Override
public void serialize(final SerializerBuffer ser, final Target target) throws ValueSerializationException, NoSuchTypeException {
this.serialize(ser);
}

@Override
public void serialize(final SerializerBuffer ser) throws ValueSerializationException {
ser.writeU16(index);
ser.writeU32(offset);
}

@Override
public void deserialize(final DeserializerBuffer deser) throws ValueDeserializationException {
this.index = deser.readU16();
this.offset = deser.readU32();
}

/**
* Obtains the fields value converted to the specified type.
*
* @param clazz the type to convert the value's bytes to
* @param <T> the expected type
* @return the bytes converted to value of type clazz
* @throws ValueDeserializationException if the value cannot be converted to the specified type
*/
@SuppressWarnings("unchecked")
public <T> T getValue(final Class<T> clazz) throws ValueDeserializationException {
final DeserializerBuffer deserializerBuffer = new DeserializerBuffer(this.value);

if (clazz.isAssignableFrom(DeserializableObject.class)) {
try {
final T value = clazz.getDeclaredConstructor().newInstance();
((DeserializableObject) value).deserialize(deserializerBuffer);
return value;
} catch (Exception e) {
throw new ValueDeserializationException("Unsupported type " + clazz.getName(), e);
}
} else if (clazz == byte[].class) {
return (T) deserializerBuffer.readByteArray(this.value.length);
} else if (clazz == Byte.class) {
return (T) (Byte) deserializerBuffer.readU8();
} else if (clazz == Long.class) {
return (T) (Long) deserializerBuffer.readU32();
} else if (clazz == Integer.class) {
return (T) (Integer) deserializerBuffer.readI32();
} else if (clazz == Short.class) {
return (T) (Short) deserializerBuffer.readU16();
} else if (clazz == String.class) {
return (T) deserializerBuffer.readString();
}

throw new IllegalArgumentException("Unsupported type " + clazz.getName());
}
}
Loading