Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Added calltable serialization for TransactionV1, Changing TransactionV1 structure #461

Merged
merged 8 commits into from
Dec 2, 2024
47 changes: 22 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,14 @@ import {
PrivateKey,
PublicKey,
RpcClient,
SessionTarget,
Timestamp,
TransactionEntryPoint,
TransactionScheduling,
TransactionTarget,
TransactionV1,
TransactionV1Body,
TransactionV1Header
} from 'casper-js-sdk-new';
TransactionV1Payload,
TransactionEntryPointEnum
} from 'casper-js-sdk';

const rpcHandler = new HttpHandler('http://<Node Address>:7777/rpc');
const rpcClient = new RpcClient(rpcHandler);
Expand All @@ -145,42 +144,40 @@ const paymentAmount = '20000000000000';

const pricingMode = new PricingMode();
const fixedMode = new FixedMode();
fixedMode.gasPriceTolerance = 3;
fixedMode.gasPriceTolerance = 1;
fixedMode.additionalComputationFactor = 0;
pricingMode.fixed = fixedMode;

const transactionHeader = TransactionV1Header.build({
chainName: 'casper-net-1',
timestamp,
ttl: new Duration(1800000),
initiatorAddr: new InitiatorAddr(privateKey.publicKey),
pricingMode
});

const args = Args.fromMap({
target: CLValue.newCLPublicKey(
PublicKey.fromHex(
'0202f5a92ab6da536e7b1a351406f3744224bec85d7acbab1497b65de48a1a707b64'
)
),
amount: CLValueUInt512.newCLUInt512(paymentAmount),
id: CLValueOption.newCLOption(CLValueUInt64.newCLUint64(3))
id: CLValueOption.newCLOption(CLValueUInt64.newCLUint64(3)) // memo ( optional )
});

const transactionTarget = new TransactionTarget(new SessionTarget());
const entryPoint = new TransactionEntryPoint(undefined, {});
const scheduling = new TransactionScheduling({});
const transactionTarget = new TransactionTarget({}); // Native target;
const entryPoint = new TransactionEntryPoint(
TransactionEntryPointEnum.Transfer
);
const scheduling = new TransactionScheduling({}); // Standard;

const transactionBody = TransactionV1Body.build({
args: args,
target: transactionTarget,
transactionEntryPoint: entryPoint,
transactionScheduling: scheduling,
transactionCategory: 2
const transactionPayload = TransactionV1Payload.build({
initiatorAddr: new InitiatorAddr(privateKey.publicKey),
ttl: new Duration(1800000),
args,
timestamp,
entryPoint,
scheduling,
transactionTarget,
chainName: 'casper-net-1',
pricingMode
});

const transaction = TransactionV1.makeTransactionV1(
transactionHeader,
transactionBody
transactionPayload
);
await transaction.sign(privateKey);

Expand Down
18 changes: 0 additions & 18 deletions src/types/AddressableEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,21 +152,3 @@ export class NamedEntryPoint {
@jsonMember({ name: 'name', constructor: String })
name: string;
}

/**
* Returns the numeric tag associated with a given transaction runtime version.
* Useful for distinguishing between different virtual machine versions.
*
* @param runtime - The transaction runtime to retrieve the tag for.
* @returns A number representing the tag for the given runtime.
*/
export function getRuntimeTag(runtime: TransactionRuntime): number {
switch (runtime) {
case 'VmCasperV1':
return 0;
case 'VmCasperV2':
return 1;
default:
return 0;
}
}
114 changes: 73 additions & 41 deletions src/types/Args.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { concat } from '@ethersproject/bytes';

import {
CLTypeString,
CLValue,
CLValueParser,
CLValueString,
CLValueUInt32,
IResultWithBytes
} from './clvalue';
import { jsonMapMember, jsonObject } from 'typedjson';
import { toBytesString, toBytesU32 } from './ByteConverters';

import { CLValue, CLValueParser } from './clvalue';
import { toBytesString, toBytesU32, writeInteger } from './ByteConverters';

/**
* Represents a named argument with a name and associated `CLValue`, which can be serialized to bytes.
Expand All @@ -32,24 +25,61 @@ export class NamedArg {
return concat([name, value]);
}

/**
* Converts a `NamedArg` object to a `Uint8Array` for serialization.
*
* The method encodes the name of the argument as a UTF-8 string, followed by the serialized
* bytes of its value. The resulting `Uint8Array` can be used for further processing, such as
* storage or transmission.
*
* @param source - The `NamedArg` object to serialize. It contains a name and a value.
* @returns A `Uint8Array` representing the serialized `NamedArg`.
*
* @example
* ```typescript
* const namedArg = new NamedArg("arg1", CLValue.u32(42));
* const serializedBytes = YourClass.toBytesWithNamedArg(namedArg);
* console.log(serializedBytes); // Logs the serialized bytes.
* ```
*/
public static toBytesWithNamedArg(source: NamedArg): Uint8Array {
// The buffer size is fixed at 1024 bytes based on the expected maximum size of
// encoded data, with room for edge cases. If inputs exceed this size, revisit
// the implementation.
const buffer = new ArrayBuffer(1024);
const view = new DataView(buffer);
let offset = 0;

const nameBytes = new TextEncoder().encode(source.name);
offset = writeInteger(view, offset, nameBytes.length);
new Uint8Array(buffer, offset).set(nameBytes);
offset += nameBytes.length;

const valueBytes = CLValueParser.toBytesWithType(source.value);
new Uint8Array(buffer, offset).set(valueBytes);
offset += valueBytes.length;

return new Uint8Array(buffer, 0, offset);
}

/**
* Creates a `NamedArg` instance from a byte array.
* @param bytes - The byte array to parse.
* @returns A new `NamedArg` instance.
* @throws Error if the value data is missing.
* @returns A `NamedArg` instance.
*/
public static fromBytes(bytes: Uint8Array): NamedArg {
const stringValue = CLValueString.fromBytes(bytes);
let offset = 0;

if (!stringValue.bytes) {
throw new Error('Missing data for value of named arg');
}
const nameLength = new DataView(bytes.buffer).getUint32(offset, true);
offset += 4;
const nameBytes = bytes.slice(offset, offset + nameLength);
offset += nameLength;
const name = new TextDecoder().decode(nameBytes);

const value = CLValueParser.fromBytesByType(
stringValue.bytes,
CLTypeString
);
return new NamedArg(value.result.toString(), value.result);
const valueBytes = bytes.slice(offset);
const value = CLValueParser.fromBytesWithType(valueBytes);

return new NamedArg(name, value.result);
}
}

Expand Down Expand Up @@ -156,28 +186,30 @@ export class Args {

/**
* Creates an `Args` instance from a byte array.
* Parses the byte array to extract each argument.
* @param bytes - The byte array to parse.
* @returns An object containing a new `Args` instance and any remaining bytes.
* @throws Error if there is an issue parsing the bytes.
* @returns An `Args` instance.
*/
public static fromBytes(bytes: Uint8Array): IResultWithBytes<Args> {
const uint32 = CLValueUInt32.fromBytes(bytes);
const size = uint32.result.getValue().toNumber();

let remainBytes: Uint8Array | undefined = uint32.bytes;
const res: NamedArg[] = [];
for (let i = 0; i < size; i++) {
if (!remainBytes) {
throw new Error('Error while parsing bytes');
}
const namedArg = NamedArg.fromBytes(remainBytes);
res.push(namedArg);
remainBytes = undefined;
public static fromBytes(bytes: Uint8Array): Args {
let offset = 0;

const numArgs = new DataView(bytes.buffer).getUint32(offset, true);
offset += 4;

const args = new Map<string, CLValue>();

for (let i = 0; i < numArgs; i++) {
const namedArgBytes = bytes.slice(offset);
const namedArg = NamedArg.fromBytes(namedArgBytes);

const nameLength = new DataView(namedArgBytes.buffer).getUint32(0, true);
const valueBytes = CLValueParser.toBytesWithType(namedArg.value);
const consumedBytes = 4 + nameLength + valueBytes.length;

offset += consumedBytes;

args.set(namedArg.name, namedArg.value);
}
return {
result: Args.fromNamedArgs(res),
bytes: remainBytes || Uint8Array.from([])
};

return new Args(args);
}
}
44 changes: 44 additions & 0 deletions src/types/Bid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export class ValidatorBid {
@jsonMember({ name: 'maximum_delegation_amount', constructor: Number })
maximumDelegationAmount: number;

/**
* Number of slots reserved for specific delegators
*/
@jsonMember({ name: 'reserved_slots', constructor: Number })
reservedSlots: number;

/**
* The vesting schedule for this validator’s stake.
*/
Expand Down Expand Up @@ -301,3 +307,41 @@ export class Bridge {
})
newValidatorPublicKey: PublicKey;
}

@jsonObject
/**
* Represents a reservation in the blockchain system, including delegation details and associated public keys.
*/
export class Reservation {
/**
* The delegation rate, representing the percentage of rewards allocated to the delegator.
*/
@jsonMember({ name: 'delegation_rate', constructor: Number })
delegationRate: number;

/**
* The public key of the validator associated with this reservation.
*
* This key is used to identify the validator in the blockchain system.
*/
@jsonMember({
name: 'validator_public_key',
constructor: PublicKey,
deserializer: json => PublicKey.fromJSON(json),
serializer: value => value.toJSON()
})
validatorPublicKey: PublicKey;

/**
* The public key of the delegator associated with this reservation.
*
* This key is used to identify the delegator who initiated the reservation.
*/
@jsonMember({
name: 'delegator_public_key',
constructor: PublicKey,
deserializer: json => PublicKey.fromJSON(json),
serializer: value => value.toJSON()
})
delegatorPublicKey: PublicKey;
}
15 changes: 14 additions & 1 deletion src/types/BidKind.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { jsonObject, jsonMember } from 'typedjson';
import { Bid, Bridge, Credit, Delegator, ValidatorBid } from './Bid';
import {
Bid,
Bridge,
Credit,
Delegator,
Reservation,
ValidatorBid
} from './Bid';

/**
* Represents a polymorphic bid kind, allowing for different types of bid-related entities.
Expand Down Expand Up @@ -37,4 +44,10 @@ export class BidKind {
*/
@jsonMember({ name: 'Credit', constructor: Credit })
credit?: Credit;

/**
* Represents a validator reserving a slot for specific delegator
*/
@jsonMember({ name: 'Reservation', constructor: Reservation })
reservation?: Reservation;
}
Loading
Loading