Skip to content

Commit

Permalink
fix posix slot conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
michele-nuzzi committed Jun 17, 2024
1 parent fdb6ec2 commit 76bd02d
Show file tree
Hide file tree
Showing 28 changed files with 359 additions and 136 deletions.
117 changes: 102 additions & 15 deletions packages/offchain/src/TxBuilder/GenesisInfos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,125 @@ import { CanBeUInteger, canBeUInteger } from "../utils/ints";

export interface GenesisInfos {
/**
* ### POSIX timestamp of blockchain start
* ## with **milliseconds precision**
* @deprecated use `systemStartPosixMs` instead
*
* POSIX timestamp of blockchain start
* with **milliseconds precision**
**/
readonly systemStartPOSIX: CanBeUInteger,
systemStartPOSIX?: CanBeUInteger,
/**
* ### slot duration in **milliseconds**
*
* POSIX timestamp of blockchain start
* with **milliseconds precision**
**/
readonly slotLengthInMilliseconds: CanBeUInteger
readonly systemStartPosixMs?: CanBeUInteger,
/**
* @deprecated use `slotLengthMs` instead
*
* slot duration in **milliseconds**
**/
slotLengthInMilliseconds?: CanBeUInteger
/**
* slot duration in **milliseconds**
**/
readonly slotLengthMs?: CanBeUInteger
/**
* slot number of the slot at `systemStartPosixMs` time
*
* @default 0
*/
readonly startSlotNo?: CanBeUInteger
}

export interface NormalizedGenesisInfos {
/**
*
* POSIX timestamp of blockchain start
* with **milliseconds precision**
**/
readonly systemStartPosixMs: number,
/**
* slot duration in **milliseconds**
**/
readonly slotLengthMs: number
/**
* slot number of the slot at `systemStartPosixMs` time
*/
readonly startSlotNo: number
}

export function normalizedGenesisInfos( gInfo: GenesisInfos ): NormalizedGenesisInfos
{
return Object.freeze({
systemStartPosixMs: Number( gInfo.systemStartPosixMs ?? gInfo.systemStartPOSIX ),
slotLengthMs: Number( gInfo.slotLengthMs ?? gInfo.slotLengthInMilliseconds ),
startSlotNo: Number( gInfo.startSlotNo ?? 0 )
} as NormalizedGenesisInfos);
}

export const defaultPreviewGenesisInfos: GenesisInfos = Object.freeze({
systemStartPOSIX: 1666656000_000,
slotLengthInMilliseconds: 1000
systemStartPOSIX : 1_666_656_000_000,
systemStartPosixMs: 1_666_656_000_000,
slotLengthInMilliseconds: 1000,
slotLengthMs : 1000,
startSlotNo: 0
} as GenesisInfos);

export const defaultPreprodGenesisInfos: GenesisInfos = Object.freeze({
systemStartPOSIX: 1666656000_000,
slotLengthInMilliseconds: 1000
systemStartPOSIX : 1_654_041_600_000 + 1_728_000_000,
systemStartPosixMs: 1_654_041_600_000 + 1_728_000_000,
slotLengthInMilliseconds: 1000,
slotLengthMs : 1000,
startSlotNo: 86400
} as GenesisInfos);

export const defaultMainnetGenesisInfos: GenesisInfos = Object.freeze({
systemStartPOSIX: 1506203091_000,
slotLengthInMilliseconds: 1000
systemStartPOSIX : 1_596_059_091_000,
systemStartPosixMs: 1_596_059_091_000,
slotLengthInMilliseconds: 1000,
slotLengthMs : 1000,
startSlotNo: 4492800
} as GenesisInfos);

export function isGenesisInfos( stuff: any ): stuff is GenesisInfos
{
return (
typeof stuff === "object" && stuff !== null &&
hasOwn( stuff, "systemStartPOSIX" ) &&
canBeUInteger( stuff.systemStartPOSIX ) &&
hasOwn( stuff, "slotLengthInMilliseconds" ) &&
canBeUInteger( stuff.slotLengthInMilliseconds )
((
hasOwn( stuff, "systemStartPOSIX" ) &&
canBeUInteger( stuff.systemStartPOSIX )
) || (
hasOwn( stuff, "systemStartPosixMs" ) &&
canBeUInteger( stuff.systemStartPosixMs )
)) &&
((
hasOwn( stuff, "slotLengthInMilliseconds" ) &&
canBeUInteger( stuff.slotLengthInMilliseconds )
) || (
hasOwn( stuff, "slotLengthMs" ) &&
canBeUInteger( stuff.slotLengthMs )
)) && (
hasOwn( stuff, "startSlotNo" ) &&
canBeUInteger( stuff.startSlotNo )
)

);
}

export function isNormalizedGenesisInfos( stuff: any ): stuff is NormalizedGenesisInfos
{
return (
typeof stuff === "object" && stuff !== null &&
(
hasOwn( stuff, "systemStartPosixMs" ) &&
(typeof stuff.systemStartPosixMs === "number")
) &&
(
hasOwn( stuff, "slotLengthMs" ) &&
canBeUInteger( stuff.slotLengthMs )
) && (
hasOwn( stuff, "startSlotNo" ) &&
canBeUInteger( stuff.startSlotNo )
)
);
}
35 changes: 21 additions & 14 deletions packages/offchain/src/TxBuilder/TxBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fromHex, fromUtf8, isUint8Array, lexCompare, toHex } from "@harmoniclabs/uint8array-utils";
import { keepRelevant } from "./keepRelevant";
import { GenesisInfos, isGenesisInfos } from "./GenesisInfos";
import { GenesisInfos, NormalizedGenesisInfos, defaultMainnetGenesisInfos, defaultPreprodGenesisInfos, isGenesisInfos, isNormalizedGenesisInfos, normalizedGenesisInfos } from "./GenesisInfos";
import { isCostModelsV2, isCostModelsV1, defaultV2Costs, defaultV1Costs, costModelsToLanguageViewCbor, isCostModelsV3, defaultV3Costs } from "@harmoniclabs/cardano-costmodels-ts";
import { NetworkT, ProtocolParameters, isPartialProtocolParameters, Tx, Value, ValueUnits, TxOut, TxRedeemerTag, txRdmrTagToString, ScriptType, UTxO, VKeyWitness, Script, BootstrapWitness, TxRedeemer, Hash32, TxIn, Hash28, AuxiliaryData, TxWitnessSet, getNSignersNeeded, txRedeemerTagToString, ScriptDataHash, Address, AddressStr, TxBody, CredentialType, canBeHash32, VotingProcedures, ProposalProcedure } from "@harmoniclabs/cardano-ledger-ts";
import { CborString, CborPositiveRational, Cbor, CborArray, CanBeCborString } from "@harmoniclabs/cbor";
Expand Down Expand Up @@ -55,7 +55,7 @@ function getScriptLikeUplc( scriptLike: ScriptLike ): UPLCTerm
export class TxBuilder
{
readonly protocolParamters!: ValidatedTxBuilderProtocolParams
readonly genesisInfos?: GenesisInfos
readonly genesisInfos?: NormalizedGenesisInfos

setGenesisInfos!: ( geneisInfos: GenesisInfos ) => void;

Expand All @@ -69,11 +69,11 @@ export class TxBuilder
genesisInfos?: GenesisInfos
)
{
let _genesisInfos: GenesisInfos | undefined = undefined;
let _genesisInfos: NormalizedGenesisInfos | undefined = undefined;
const _setGenesisInfos = ( genInfos: GenesisInfos ): void => {
if( !isGenesisInfos( genInfos ) ) return;

_genesisInfos = freezeAll( genInfos );
_genesisInfos = freezeAll( normalizedGenesisInfos( genInfos ) );
}
_setGenesisInfos( genesisInfos! );
Object.defineProperties(
Expand Down Expand Up @@ -172,38 +172,36 @@ export class TxBuilder
* @param slotN number of the slot
* @returns POSIX time in **milliseconds**
*/
slotToPOSIX( slot: CanBeUInteger ): number
slotToPOSIX( slot: CanBeUInteger, genesisInfos?: GenesisInfos ): number
{
const gInfos = this.genesisInfos;
const gInfos = genesisInfos ? normalizedGenesisInfos( genesisInfos ) : this.genesisInfos;
if( gInfos === undefined )
{
throw new Error("can't convert slot to POSIX time because genesis infos are missing");
}

return slotToPOSIX(
unsafeForceUInt( slot ),
unsafeForceUInt( gInfos.systemStartPOSIX ),
unsafeForceUInt( gInfos.slotLengthInMilliseconds )
)
gInfos
);
}

/**
*
* @param POSIX POSIX time in milliseconds
*/
posixToSlot( POSIX: CanBeUInteger ): number
posixToSlot( POSIX: CanBeUInteger, genesisInfos?: GenesisInfos ): number
{
const gInfos = this.genesisInfos;
const gInfos = genesisInfos ? normalizedGenesisInfos( genesisInfos ) : this.genesisInfos;
if( gInfos === undefined )
{
throw new Error("can't convert POSIX to slot time because genesis infos are missing");
}

return POSIXToSlot(
unsafeForceUInt( POSIX ),
unsafeForceUInt( gInfos.systemStartPOSIX ),
unsafeForceUInt( gInfos.slotLengthInMilliseconds )
)
gInfos
);
}

/**
Expand Down Expand Up @@ -599,6 +597,15 @@ export class TxBuilder
if( !change ) change = { address: changeAddress };

const network = changeAddress.network;
if( !isNormalizedGenesisInfos( this.genesisInfos ) )
{
this.setGenesisInfos(
network === "mainnet" ?
defaultMainnetGenesisInfos :
defaultPreprodGenesisInfos
);
}

const undef: undefined = void 0;

// filter inputs so that are unique
Expand Down
24 changes: 13 additions & 11 deletions packages/offchain/src/toOnChain/getTxIntervalData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Data, DataConstr, DataI } from "@harmoniclabs/plutus-data";
import { GenesisInfos, isGenesisInfos } from "../TxBuilder/GenesisInfos";
import { GenesisInfos, NormalizedGenesisInfos, isGenesisInfos, isNormalizedGenesisInfos, normalizedGenesisInfos } from "../TxBuilder/GenesisInfos";
import { unsafeForceUInt } from "../utils/ints";

/**
Expand All @@ -9,9 +9,11 @@ import { unsafeForceUInt } from "../utils/ints";
* @param slotLenMs milliseconds per slot
* @returns
*/
export function POSIXToSlot( POSIX: number, sysStartPOSIX: number, slotLenMs: number ): number
export function POSIXToSlot( unixTime: number, gInfos: NormalizedGenesisInfos ): number
{
return Math.floor( (POSIX - sysStartPOSIX) / slotLenMs );
const timePassed = unixTime - gInfos.systemStartPosixMs;
const slotsPassed = Math.floor(timePassed / gInfos.slotLengthMs);
return slotsPassed + gInfos.startSlotNo;
}

/**
Expand All @@ -21,9 +23,10 @@ export function POSIXToSlot( POSIX: number, sysStartPOSIX: number, slotLenMs: nu
* @param slotLenMs milliseconds per slot
* @returns
*/
export function slotToPOSIX( slotN: number, sysStartPOSIX: number, slotLenMs: number ): number
export function slotToPOSIX( slot: number, gInfos: NormalizedGenesisInfos ): number
{
return sysStartPOSIX + (slotLenMs * slotN);
const msAfterBegin = (slot - gInfos.startSlotNo) * gInfos.slotLengthMs;
return gInfos.systemStartPosixMs + msAfterBegin;
}

export function getTxIntervalData(
Expand All @@ -32,6 +35,7 @@ export function getTxIntervalData(
geneisInfos: GenesisInfos | undefined
): DataConstr
{
geneisInfos = geneisInfos ? normalizedGenesisInfos( geneisInfos ) : undefined;
let lowerBoundData: Data | undefined = undefined;

if( startSlot === undefined )
Expand All @@ -40,7 +44,7 @@ export function getTxIntervalData(
}
else
{
if( !isGenesisInfos( geneisInfos ) )
if( !isNormalizedGenesisInfos( geneisInfos ) )
{
throw new Error("missing genesis infos requried to translate slot number to posix")
}
Expand All @@ -51,8 +55,7 @@ export function getTxIntervalData(
new DataI(
slotToPOSIX(
unsafeForceUInt( startSlot ),
unsafeForceUInt( geneisInfos.systemStartPOSIX ),
unsafeForceUInt( geneisInfos.slotLengthInMilliseconds )
geneisInfos
)
)
]
Expand All @@ -69,7 +72,7 @@ export function getTxIntervalData(
}
else
{
if( !isGenesisInfos( geneisInfos ) )
if( !isNormalizedGenesisInfos( geneisInfos ) )
{
throw new Error("missing genesis infos requried to translate slot number to posix")
}
Expand All @@ -80,8 +83,7 @@ export function getTxIntervalData(
new DataI(
slotToPOSIX(
unsafeForceUInt( endSlot ),
unsafeForceUInt( geneisInfos.systemStartPOSIX ),
unsafeForceUInt( geneisInfos.slotLengthInMilliseconds )
geneisInfos
)
)
]
Expand Down
3 changes: 3 additions & 0 deletions packages/onchain/src/IR/IRNodes/IRLetted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ const defaultLettedMeta: IRLettedMeta = freezeAll({
forceHoist: false
});

let n_hash_access = 0;

export class IRLetted
implements Cloneable<IRLetted>, IHash, IIRParent, ToJson, IRLettedMetadata
{
Expand Down Expand Up @@ -151,6 +153,7 @@ export class IRLetted
Object.defineProperty(
this, "hash", {
get: () => {
++n_hash_access > 6300 && console.log("n_hash_access", n_hash_access);
if(!( hash instanceof Uint8Array ))
{
const normalized = getNormalizedLettedArgs( this.dbn, _value );
Expand Down
36 changes: 26 additions & 10 deletions packages/onchain/src/IR/toUPLC/_internal/_irToUplc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ export function _irToUplc(
max_idx: number
}
{
if(!(ir instanceof IRConst))
{
// console.log("_irToUplc", ir, node_index, Date.now());
}
else {
// if( ir.parent )
// {
// console.log(ir.parent ? Object.getPrototypeOf( ir.parent ).constructor.name : undefined, Date.now() );
// }
// else
// {
// console.log( Error().stack );
// }
}

if( ir instanceof IRConst )
{
return {
term: new UPLCConst(
termTyToConstTy( ir.type ),
ir.value as any
),
max_idx: node_index
};
}

if( ir instanceof IRVar ) return {
term: new UPLCVar( ir.dbn ),
max_idx: node_index
Expand Down Expand Up @@ -68,16 +94,6 @@ export function _irToUplc(
};
}

if( ir instanceof IRConst )
{
return {
term: new UPLCConst(
termTyToConstTy( ir.type ),
ir.value as any
),
max_idx: node_index
};
}
if( ir instanceof IRNative )
{
if( ir.tag < 0 )
Expand Down
Loading

0 comments on commit 76bd02d

Please sign in to comment.