convex.core.transactions.Multi 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.transactions;
import convex.core.ErrorCodes;
import convex.core.Result;
import convex.core.data.ACell;
import convex.core.data.AVector;
import convex.core.data.AccountStatus;
import convex.core.data.Address;
import convex.core.data.Blob;
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.data.prim.CVMLong;
import convex.core.exceptions.BadFormatException;
import convex.core.exceptions.InvalidDataException;
import convex.core.lang.Context;
import convex.core.lang.RecordFormat;
/**
* The Multi class enables multiple child transactions to be grouped into a single
* wrapper transaction with useful joint execution semantics.
*
* Important notes:
* - Child transactions must either have the same origin address, or be
* for accounts controlled by the top level origin Address
* - Sequence numbers on child transactions are ignored
* - All transactions currently share the same juice limit / memory allowance
*/
public class Multi extends ATransaction {
protected Ref> txs;
private int mode;
/**
* Mode to execute and report all transactions, regardless of outcome.
* Equivalent to executing transactions independently.
*/
public static final int MODE_ANY=0;
/**
* Mode to execute all transactions iff all succeed.
* Will rollback state changes if any fail.
*/
public static final int MODE_ALL=1;
/**
* Mode to execute up to the first transaction succeeds.
* State changes resulting from the first successful transaction only will be applied.
*/
public static final int MODE_FIRST=2;
/**
* Mode to execute until the transaction fails.
* Transactions beyond the first failure will not be attempted.
*/
public static final int MODE_UNTIL=3;
private static final Keyword[] KEYS = new Keyword[] { Keywords.ORIGIN, Keywords.SEQUENCE,Keywords.MODE,Keywords.TXS};
private static final RecordFormat FORMAT = RecordFormat.of(KEYS);
protected Multi(Address origin, long sequence, int mode, Ref> txs) {
super(FORMAT.count(), origin, sequence);
this.mode=mode;
this.txs=txs;
}
public static Multi create(Address origin, long sequence, int mode, ATransaction... txs) {
AVector v= Vectors.create(txs);
return new Multi(origin,sequence,mode,v.getRef());
}
public int getMode() {
return mode;
}
@Override
public int encode(byte[] bs, int pos) {
bs[pos++] = Tag.MULTI;
return encodeRaw(bs,pos);
}
@Override
public int encodeRaw(byte[] bs, int pos) {
pos = super.encodeRaw(bs,pos); // origin, sequence
pos = Format.writeVLCCount(bs,pos, mode);
pos = txs.encode(bs, pos);
return pos;
}
public static Multi read(Blob b, int pos) throws BadFormatException {
int epos=pos+1; // skip tag
long aval=Format.readVLCCount(b,epos);
Address origin=Address.create(aval);
epos+=Format.getVLCCountLength(aval);
long sequence = Format.readVLCCount(b,epos);
epos+=Format.getVLCCountLength(sequence);
long mode = Format.readVLCCount(b,epos);
if (!isValidMode(mode)) throw new BadFormatException("Invalid Multi transaction mode: "+mode);
epos+=Format.getVLCCountLength(mode);
Ref> txs=Format.readRef(b, epos);
epos+=txs.getEncodingLength();
Multi result=new Multi(origin,sequence,(int)mode,txs);
result.attachEncoding(b.slice(pos,epos));
return result;
}
private static boolean isValidMode(long mode) {
return (mode>=MODE_ANY)&&(mode<=MODE_UNTIL);
}
@Override
public int estimatedEncodingSize() {
return 30+Format.MAX_EMBEDDED_LENGTH;
}
@Override
public Context apply(Context ctx) {
// save initial context, we might need this for rollback
Context ictx=ctx.fork();
AVector ts=txs.getValue();
// Context> initialContext=ctx.fork();
long n=ts.count();
AVector rs=Vectors.empty();
for (int i=0; iMODE_UNTIL)) throw new InvalidDataException("Illegal mode: "+mode,this);
}
@Override
public ACell get(Keyword key) {
if (Keywords.ORIGIN.equals(key)) return origin;
if (Keywords.SEQUENCE.equals(key)) return CVMLong.create(sequence);
if (Keywords.MODE.equals(key)) return CVMLong.create(mode);
if (Keywords.TXS.equals(key)) return txs.getValue();
return null;
}
@Override
public int getRefCount() {
// Always just one Ref
return 1;
}
@SuppressWarnings("unchecked")
@Override
public Ref getRef(int i) {
if (i==0) return (Ref) txs;
throw new IndexOutOfBoundsException(i);
}
@SuppressWarnings("unchecked")
@Override
public Multi updateRefs(IRefFunction func) {
Ref> ntxs=(Ref>) func.apply(txs);
if (ntxs==txs) return this;
return new Multi(origin,sequence,mode,ntxs);
}
@Override
public RecordFormat getFormat() {
return FORMAT;
}
}