convex.core.Receipt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
The newest version!
package convex.core;
import convex.core.data.ACell;
import convex.core.data.ARecord;
import convex.core.data.AVector;
import convex.core.data.Blob;
import convex.core.data.Cells;
import convex.core.data.Format;
import convex.core.data.IRefFunction;
import convex.core.data.Keyword;
import convex.core.data.Keywords;
import convex.core.data.Ref;
import convex.core.data.Tag;
import convex.core.data.Vectors;
import convex.core.exceptions.BadFormatException;
import convex.core.exceptions.InvalidDataException;
import convex.core.lang.RecordFormat;
/**
* Transaction Receipt record.
*
* This forms part of the official CVM state transition, so must be correctly computed and encoded
*/
public class Receipt extends ARecord {
private static final Keyword[] RECEIPT_KEYS = new Keyword[] { Keywords.RESULT, Keywords.ERROR, Keywords.LOG};
private static final RecordFormat FORMAT = RecordFormat.of(RECEIPT_KEYS);
private static final long RECEIPT_LENGTH=RECEIPT_KEYS.length;
private static final AVector> EMPTY_LOG = Vectors.empty();
private final boolean isError;
private final ACell result;
private final AVector> log; // Log vector, or null if empty
protected Receipt(boolean error, ACell value, AVector> log) {
super(RECEIPT_LENGTH);
this.isError=error;
this.result=value;
this.log=log;
}
public static Receipt create(ACell result) {
return new Receipt(false,result,null);
}
public static Receipt create(boolean isError,ACell value, AVector> log) {
if ((log!=null)&&(log.isEmpty())) log=null;
return new Receipt(isError,value,log);
}
public static Receipt createError(ACell errorCode) {
return new Receipt(true,errorCode,null);
}
@Override
public ACell get(Keyword k) {
if (Keywords.RESULT.equals(k)) return getResult();
if (Keywords.ERROR.equals(k)) return getErrorCode();
if (Keywords.LOG.equals(k)) return getLog();
return null;
}
private AVector> getLog() {
return (log==null)?EMPTY_LOG:log;
}
/**
* Gets the error code of this Receipt, or null if not an error
* @return Error code (probably a Keyword or nil)
*/
public ACell getErrorCode() {
if (!isError) return null;
return result;
}
/**
* Gets the result value of this Receipt, or null if an error
* @return Result from this Receipt
*/
public ACell getResult() {
if (isError) return null;
return result;
}
@Override
public byte getTag() {
int tag=Tag.RECEIPT+(isError?Tag.RECEIPT_ERROR_MASK:0)+((log==null)?0:Tag.RECEIPT_LOG_MASK);
return (byte)tag;
}
@Override public final boolean isCVMValue() {
// Receipts exist outside CVM only
return false;
}
@Override
public RecordFormat getFormat() {
return FORMAT;
}
@Override
public void validateCell() throws InvalidDataException {
if (result!=null) {
if (!result.isCVMValue()) throw new InvalidDataException("Receipt Result must be CVM value",this);
result.validateCell();
}
}
@Override
public boolean equals(ACell a) {
if (!(a instanceof Receipt)) return false;
return equals((Receipt) a);
}
public boolean equals(Receipt a) {
if (a==this) return true;
if (this.isError!=a.isError) return false;
if (!Cells.equals(result, a.result)) return false;
if (!Cells.equals(log, a.log)) return false;
return true;
}
@Override
public int encode(byte[] bs, int pos) {
bs[pos]=getTag();
return encodeRaw(bs,pos+1);
}
@Override
public int encodeRaw(byte[] bs, int pos) {
pos=Format.write(bs, pos, result);
if (log!=null) pos=log.encode(bs, pos);
return pos;
}
public static Receipt read(byte tag,Blob b, int pos) throws BadFormatException {
int epos=pos+1; // skip tag
boolean isError=((tag&Tag.RECEIPT_ERROR_MASK)!=0);
boolean hasLog=((tag&Tag.RECEIPT_LOG_MASK)!=0);
ACell result = Format.read(b,epos);
epos+=Format.getEncodingLength(result);
AVector> log=null;
if (hasLog) {
log = Vectors.read(b, epos);
if ((log==null)||(log.isEmpty())) throw new BadFormatException("Expected non-empty log");
epos+=Format.getEncodingLength(log);
}
Receipt receipt=new Receipt(isError,result,log);
receipt.attachEncoding(b.slice(pos, epos));
return receipt;
}
@Override
public Receipt updateRefs(IRefFunction func) {
ACell newResult=Ref.update(result, func);
AVector> newLog=(AVector>) Ref.update(log, func);
if ((log==newLog)&&(result==newResult)) return this;
return new Receipt(isError,newResult,newLog);
}
@Override
public int getRefCount() {
return Cells.refCount(result)+Cells.refCount(log);
}
@Override
public Ref getRef(int i) {
int rr=Cells.refCount(result);
if (i