Skip to content

Commit

Permalink
Merge pull request #461 from casper-ecosystem/restructuring_transacti…
Browse files Browse the repository at this point in the history
…on_datatype

feature: Added calltable serialization for TransactionV1, Changing TransactionV1 structure
  • Loading branch information
alexmyshchyshyn authored Dec 2, 2024
2 parents be29c68 + 961d829 commit 4397de1
Show file tree
Hide file tree
Showing 25 changed files with 1,879 additions and 1,331 deletions.
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

0 comments on commit 4397de1

Please sign in to comment.