@xyo-network/chain-producer 1.17.7 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/ProducerActor.d.ts +119 -121
- package/dist/node/ProducerActor.d.ts.map +1 -1
- package/dist/node/index.mjs +84 -131
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/runProducer.d.ts +3 -2
- package/dist/node/runProducer.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/ProducerActor.ts +66 -102
- package/src/runProducer.ts +42 -69
|
@@ -1,136 +1,133 @@
|
|
|
1
1
|
import type { Attributes, Counter } from '@opentelemetry/api';
|
|
2
2
|
import type { CreatableName } from '@xylabs/sdk-js';
|
|
3
|
-
import { AccountInstance } from '@xyo-network/account-model';
|
|
4
3
|
import { Actor, type ActorParams } from '@xyo-network/chain-orchestration';
|
|
5
|
-
import type {
|
|
6
|
-
import { AccountBalanceViewer,
|
|
7
|
-
export type ProducerActorParams = ActorParams
|
|
8
|
-
account: AccountInstance;
|
|
9
|
-
blockRewardViewer: BlockRewardViewer;
|
|
10
|
-
chainId: ChainId;
|
|
11
|
-
config: Config;
|
|
12
|
-
runners: {
|
|
13
|
-
mempoolRunner: MempoolRunner;
|
|
14
|
-
};
|
|
15
|
-
viewers: {
|
|
16
|
-
accountBalanceViewer: AccountBalanceViewer;
|
|
17
|
-
blockViewer: BlockViewer;
|
|
18
|
-
mempoolViewer: MempoolViewer;
|
|
19
|
-
stakeTotalsViewer: StakeTotalsViewer;
|
|
20
|
-
time: TimeSyncViewer;
|
|
21
|
-
};
|
|
22
|
-
}>;
|
|
4
|
+
import type { ChainStakeIntent, HydratedBlockWithHashMeta, XL1BlockNumber } from '@xyo-network/xl1-protocol';
|
|
5
|
+
import { AccountBalanceViewer, BlockRunner, BlockViewer, MempoolRunner, MempoolViewer, StakeTotalsViewer } from '@xyo-network/xl1-protocol-sdk';
|
|
6
|
+
export type ProducerActorParams = ActorParams;
|
|
23
7
|
export declare class ProducerActor extends Actor<ProducerActorParams> {
|
|
24
|
-
|
|
25
|
-
protected
|
|
26
|
-
protected
|
|
27
|
-
protected
|
|
28
|
-
protected
|
|
29
|
-
protected
|
|
30
|
-
protected
|
|
8
|
+
static readonly RedeclarationWindow = 10000;
|
|
9
|
+
protected _lastProducedBlock?: HydratedBlockWithHashMeta;
|
|
10
|
+
protected _lastRedeclarationIntent?: ChainStakeIntent;
|
|
11
|
+
protected _metricAttributes?: Attributes;
|
|
12
|
+
protected _producerActorBlockProductionAttempts?: Counter<Attributes>;
|
|
13
|
+
protected _producerActorBlockProductionChecks?: Counter<Attributes>;
|
|
14
|
+
protected _producerActorBlocksProduced?: Counter<Attributes>;
|
|
15
|
+
protected _producerActorBlocksPublished?: Counter<Attributes>;
|
|
16
|
+
private _accountBalanceViewer?;
|
|
17
|
+
private _blockRunner?;
|
|
18
|
+
private _blockViewer?;
|
|
19
|
+
private _chainId?;
|
|
20
|
+
private _mempoolRunner?;
|
|
21
|
+
private _mempoolViewer?;
|
|
31
22
|
private _produceBlockMutex;
|
|
32
|
-
private
|
|
33
|
-
protected get account(): AccountInstance;
|
|
23
|
+
private _stakeTotalsViewer?;
|
|
34
24
|
protected get accountBalanceViewer(): AccountBalanceViewer;
|
|
25
|
+
protected get blockRunner(): BlockRunner;
|
|
35
26
|
protected get blockViewer(): BlockViewer;
|
|
36
27
|
protected get chainId(): import("@xylabs/sdk-js").Hex;
|
|
37
|
-
protected get config(): {
|
|
38
|
-
api: {
|
|
39
|
-
host: string;
|
|
40
|
-
initRewardsCache: boolean;
|
|
41
|
-
port: number;
|
|
42
|
-
mnemonic?: string | undefined;
|
|
43
|
-
};
|
|
44
|
-
bridge: {
|
|
45
|
-
chainRpcApiUrl: string;
|
|
46
|
-
feeFixed: import("@xylabs/sdk-js").Hex;
|
|
47
|
-
feeRateBasisPoints: number;
|
|
48
|
-
host: string;
|
|
49
|
-
maxBridgeAmount: import("@xylabs/sdk-js").Hex;
|
|
50
|
-
minBridgeAmount: import("@xylabs/sdk-js").Hex;
|
|
51
|
-
port: number;
|
|
52
|
-
remoteBridgeContractAddress: import("@xylabs/sdk-js").Address;
|
|
53
|
-
remoteChainId: import("@xylabs/sdk-js").Hex;
|
|
54
|
-
remoteTokenAddress: import("@xylabs/sdk-js").Hex;
|
|
55
|
-
escrowAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
56
|
-
feesAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
57
|
-
mnemonic?: string | undefined;
|
|
58
|
-
xl1ChainId?: import("@xylabs/sdk-js").Hex | undefined;
|
|
59
|
-
xl1TokenAddress?: import("@xylabs/sdk-js").Hex | undefined;
|
|
60
|
-
};
|
|
61
|
-
chain: {
|
|
62
|
-
id?: string | undefined;
|
|
63
|
-
genesisRewardAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
64
|
-
};
|
|
65
|
-
evm: {
|
|
66
|
-
chainId?: string | undefined;
|
|
67
|
-
infura?: {
|
|
68
|
-
projectId?: string | undefined;
|
|
69
|
-
projectSecret?: string | undefined;
|
|
70
|
-
} | undefined;
|
|
71
|
-
jsonRpc?: {
|
|
72
|
-
url?: string | undefined;
|
|
73
|
-
} | undefined;
|
|
74
|
-
};
|
|
75
|
-
mempool: {
|
|
76
|
-
enabled: boolean;
|
|
77
|
-
host: string;
|
|
78
|
-
port: number;
|
|
79
|
-
mnemonic?: string | undefined;
|
|
80
|
-
};
|
|
81
|
-
producer: {
|
|
82
|
-
heartbeatInterval: number;
|
|
83
|
-
minStake: number;
|
|
84
|
-
port: number;
|
|
85
|
-
allowlist?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
86
|
-
disableIntentRedeclaration?: boolean | undefined;
|
|
87
|
-
healthCheckPort?: number | undefined;
|
|
88
|
-
mnemonic?: string | undefined;
|
|
89
|
-
rewardAddress?: string | undefined;
|
|
90
|
-
};
|
|
91
|
-
rewardRedemptionApi: {
|
|
92
|
-
chainRpcApiUrl: string;
|
|
93
|
-
host: string;
|
|
94
|
-
port: number;
|
|
95
|
-
mnemonic?: string | undefined;
|
|
96
|
-
};
|
|
97
|
-
services: {
|
|
98
|
-
accountBalanceViewerEndpoint?: string | undefined;
|
|
99
|
-
apiEndpoint?: string | undefined;
|
|
100
|
-
};
|
|
101
|
-
storage: {
|
|
102
|
-
mongo?: {
|
|
103
|
-
connectionString?: string | undefined;
|
|
104
|
-
database?: string | undefined;
|
|
105
|
-
domain?: string | undefined;
|
|
106
|
-
password?: string | undefined;
|
|
107
|
-
username?: string | undefined;
|
|
108
|
-
} | undefined;
|
|
109
|
-
root?: string | undefined;
|
|
110
|
-
};
|
|
111
|
-
telemetry: {
|
|
112
|
-
metrics?: {
|
|
113
|
-
scrape: {
|
|
114
|
-
path: string;
|
|
115
|
-
port: number;
|
|
116
|
-
};
|
|
117
|
-
} | undefined;
|
|
118
|
-
otel?: {
|
|
119
|
-
otlpEndpoint?: string | undefined;
|
|
120
|
-
} | undefined;
|
|
121
|
-
};
|
|
122
|
-
validation: {
|
|
123
|
-
allowedRewardRedeemers?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
124
|
-
allowedRewardEscrowAccountSigners?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
125
|
-
};
|
|
126
|
-
logLevel: "info" | "warn" | "error" | "log" | "debug" | "trace";
|
|
127
|
-
silent: boolean;
|
|
128
|
-
};
|
|
129
28
|
protected get mempoolRunner(): MempoolRunner;
|
|
130
29
|
protected get mempoolViewer(): MempoolViewer;
|
|
131
|
-
protected get producer(): BlockProducerService;
|
|
132
30
|
protected get stakeTotalsViewer(): StakeTotalsViewer;
|
|
133
31
|
static paramsHandler<T extends ActorParams>(params?: Partial<T>): Promise<{
|
|
32
|
+
account: T["account"] | import("@xyo-network/wallet-model").WalletInstance;
|
|
33
|
+
context: T["context"] | {
|
|
34
|
+
config: {
|
|
35
|
+
api: {
|
|
36
|
+
host: string;
|
|
37
|
+
initRewardsCache: boolean;
|
|
38
|
+
port: number;
|
|
39
|
+
mnemonic?: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
bridge: {
|
|
42
|
+
chainRpcApiUrl: string;
|
|
43
|
+
feeFixed: import("@xylabs/sdk-js").Hex;
|
|
44
|
+
feeRateBasisPoints: number;
|
|
45
|
+
host: string;
|
|
46
|
+
maxBridgeAmount: import("@xylabs/sdk-js").Hex;
|
|
47
|
+
minBridgeAmount: import("@xylabs/sdk-js").Hex;
|
|
48
|
+
port: number;
|
|
49
|
+
redisHost: string;
|
|
50
|
+
redisPort: number;
|
|
51
|
+
remoteBridgeContractAddress: import("@xylabs/sdk-js").Address;
|
|
52
|
+
remoteChainId: import("@xylabs/sdk-js").Hex;
|
|
53
|
+
remoteTokenAddress: import("@xylabs/sdk-js").Hex;
|
|
54
|
+
remoteChainWalletPrivateKey: import("@xylabs/sdk-js").Hex;
|
|
55
|
+
escrowAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
56
|
+
feesAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
57
|
+
mnemonic?: string | undefined;
|
|
58
|
+
xl1ChainId?: import("@xylabs/sdk-js").Hex | undefined;
|
|
59
|
+
xl1TokenAddress?: import("@xylabs/sdk-js").Hex | undefined;
|
|
60
|
+
};
|
|
61
|
+
chain: {
|
|
62
|
+
id?: import("@xylabs/sdk-js").Hex | undefined;
|
|
63
|
+
genesisRewardAddress?: import("@xylabs/sdk-js").Address | undefined;
|
|
64
|
+
};
|
|
65
|
+
evm: {
|
|
66
|
+
chainId?: string | undefined;
|
|
67
|
+
infura?: {
|
|
68
|
+
projectId?: string | undefined;
|
|
69
|
+
projectSecret?: string | undefined;
|
|
70
|
+
} | undefined;
|
|
71
|
+
jsonRpc?: {
|
|
72
|
+
url?: string | undefined;
|
|
73
|
+
} | undefined;
|
|
74
|
+
};
|
|
75
|
+
mempool: {
|
|
76
|
+
enabled: boolean;
|
|
77
|
+
host: string;
|
|
78
|
+
port: number;
|
|
79
|
+
mnemonic?: string | undefined;
|
|
80
|
+
};
|
|
81
|
+
producer: {
|
|
82
|
+
heartbeatInterval: number;
|
|
83
|
+
minStake: number;
|
|
84
|
+
port: number;
|
|
85
|
+
allowlist?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
86
|
+
disableIntentRedeclaration?: boolean | undefined;
|
|
87
|
+
healthCheckPort?: number | undefined;
|
|
88
|
+
mnemonic?: string | undefined;
|
|
89
|
+
rewardAddress?: string | undefined;
|
|
90
|
+
};
|
|
91
|
+
rewardRedemptionApi: {
|
|
92
|
+
chainRpcApiUrl: string;
|
|
93
|
+
host: string;
|
|
94
|
+
port: number;
|
|
95
|
+
mnemonic?: string | undefined;
|
|
96
|
+
};
|
|
97
|
+
services: {
|
|
98
|
+
accountBalanceViewerEndpoint: string;
|
|
99
|
+
apiEndpoint: string;
|
|
100
|
+
};
|
|
101
|
+
storage: {
|
|
102
|
+
mongo?: {
|
|
103
|
+
connectionString?: string | undefined;
|
|
104
|
+
database?: string | undefined;
|
|
105
|
+
domain?: string | undefined;
|
|
106
|
+
password?: string | undefined;
|
|
107
|
+
username?: string | undefined;
|
|
108
|
+
} | undefined;
|
|
109
|
+
root?: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
telemetry: {
|
|
112
|
+
metrics?: {
|
|
113
|
+
scrape: {
|
|
114
|
+
path: string;
|
|
115
|
+
port: number;
|
|
116
|
+
};
|
|
117
|
+
} | undefined;
|
|
118
|
+
otel?: {
|
|
119
|
+
otlpEndpoint?: string | undefined;
|
|
120
|
+
} | undefined;
|
|
121
|
+
};
|
|
122
|
+
validation: {
|
|
123
|
+
allowedRewardRedeemers?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
124
|
+
allowedRewardEscrowAccountSigners?: import("@xylabs/sdk-js").Address[] | undefined;
|
|
125
|
+
};
|
|
126
|
+
logLevel: "info" | "error" | "warn" | "log" | "debug" | "trace";
|
|
127
|
+
silent: boolean;
|
|
128
|
+
};
|
|
129
|
+
locator: import("@xyo-network/xl1-protocol-sdk").ProviderFactoryLocator;
|
|
130
|
+
};
|
|
134
131
|
displayName: CreatableName | NonNullable<T["displayName"]> | undefined;
|
|
135
132
|
id: CreatableName | T["id"] | undefined;
|
|
136
133
|
logger: import("@xylabs/sdk-js").Logger;
|
|
@@ -141,6 +138,7 @@ export declare class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
141
138
|
}>;
|
|
142
139
|
createHandler(): Promise<void>;
|
|
143
140
|
startHandler(): Promise<void>;
|
|
141
|
+
protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number;
|
|
144
142
|
protected produceBlock(): Promise<void>;
|
|
145
143
|
protected redeclareIntent(): Promise<void>;
|
|
146
144
|
protected submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProducerActor.d.ts","sourceRoot":"","sources":["../../src/ProducerActor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"ProducerActor.d.ts","sourceRoot":"","sources":["../../src/ProducerActor.ts"],"names":[],"mappings":"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"}
|
package/dist/node/index.mjs
CHANGED
|
@@ -2,12 +2,11 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/ProducerActor.ts
|
|
5
|
-
import {
|
|
6
|
-
import { Actor } from "@xyo-network/chain-orchestration";
|
|
7
|
-
import { createDeclarationIntent } from "@xyo-network/chain-protocol";
|
|
5
|
+
import { creatable, isDefined, isUndefined, toHex } from "@xylabs/sdk-js";
|
|
6
|
+
import { Actor, initProducerAccount } from "@xyo-network/chain-orchestration";
|
|
8
7
|
import { SimpleBlockRunner } from "@xyo-network/chain-services";
|
|
9
8
|
import { asXL1BlockNumber } from "@xyo-network/xl1-protocol";
|
|
10
|
-
import { buildTransaction } from "@xyo-network/xl1-protocol-sdk";
|
|
9
|
+
import { AccountBalanceViewerMoniker, BlockRunnerMoniker, BlockViewerMoniker, buildTransaction, createDeclarationIntent, getDefaultConfig, MempoolRunnerMoniker, MempoolViewerMoniker, StakeTotalsViewerMoniker } from "@xyo-network/xl1-protocol-sdk";
|
|
11
10
|
import { Mutex } from "async-mutex";
|
|
12
11
|
function _ts_decorate(decorators, target, key, desc) {
|
|
13
12
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -18,10 +17,11 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
18
17
|
__name(_ts_decorate, "_ts_decorate");
|
|
19
18
|
var SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true;
|
|
20
19
|
var TEN_MINUTES = 10 * 60 * 1e3;
|
|
21
|
-
var ProducerActor = class extends Actor {
|
|
20
|
+
var ProducerActor = class _ProducerActor extends Actor {
|
|
22
21
|
static {
|
|
23
22
|
__name(this, "ProducerActor");
|
|
24
23
|
}
|
|
24
|
+
static RedeclarationWindow = 1e4;
|
|
25
25
|
_lastProducedBlock;
|
|
26
26
|
_lastRedeclarationIntent;
|
|
27
27
|
_metricAttributes;
|
|
@@ -29,39 +29,44 @@ var ProducerActor = class extends Actor {
|
|
|
29
29
|
_producerActorBlockProductionChecks;
|
|
30
30
|
_producerActorBlocksProduced;
|
|
31
31
|
_producerActorBlocksPublished;
|
|
32
|
+
_accountBalanceViewer;
|
|
33
|
+
_blockRunner;
|
|
34
|
+
_blockViewer;
|
|
35
|
+
_chainId;
|
|
36
|
+
_mempoolRunner;
|
|
37
|
+
_mempoolViewer;
|
|
32
38
|
_produceBlockMutex = new Mutex();
|
|
33
|
-
|
|
34
|
-
get account() {
|
|
35
|
-
return this.params.account;
|
|
36
|
-
}
|
|
39
|
+
_stakeTotalsViewer;
|
|
37
40
|
get accountBalanceViewer() {
|
|
38
|
-
return this.
|
|
41
|
+
return this._accountBalanceViewer;
|
|
42
|
+
}
|
|
43
|
+
get blockRunner() {
|
|
44
|
+
return this._blockRunner;
|
|
39
45
|
}
|
|
40
46
|
get blockViewer() {
|
|
41
|
-
return this.
|
|
47
|
+
return this._blockViewer;
|
|
42
48
|
}
|
|
43
49
|
get chainId() {
|
|
44
|
-
return this.
|
|
45
|
-
}
|
|
46
|
-
get config() {
|
|
47
|
-
return this.params.config;
|
|
50
|
+
return this._chainId;
|
|
48
51
|
}
|
|
49
52
|
get mempoolRunner() {
|
|
50
|
-
return this.
|
|
53
|
+
return this._mempoolRunner;
|
|
51
54
|
}
|
|
52
55
|
get mempoolViewer() {
|
|
53
|
-
return this.
|
|
54
|
-
}
|
|
55
|
-
get producer() {
|
|
56
|
-
return this._producer;
|
|
56
|
+
return this._mempoolViewer;
|
|
57
57
|
}
|
|
58
58
|
get stakeTotalsViewer() {
|
|
59
|
-
return this.
|
|
59
|
+
return this._stakeTotalsViewer;
|
|
60
60
|
}
|
|
61
61
|
static async paramsHandler(params) {
|
|
62
|
+
const config = params?.context?.config ?? getDefaultConfig();
|
|
63
|
+
const account = params?.account ?? await initProducerAccount({
|
|
64
|
+
config
|
|
65
|
+
});
|
|
62
66
|
return await super.paramsHandler({
|
|
63
67
|
...params,
|
|
64
|
-
name: params?.name ?? "Producer"
|
|
68
|
+
name: params?.name ?? "Producer",
|
|
69
|
+
account
|
|
65
70
|
});
|
|
66
71
|
}
|
|
67
72
|
async createHandler() {
|
|
@@ -81,18 +86,12 @@ var ProducerActor = class extends Actor {
|
|
|
81
86
|
this._producerActorBlocksPublished = this.meter?.createCounter("producer_actor_blocks_published", {
|
|
82
87
|
description: "Number of blocks published"
|
|
83
88
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
mempoolViewer: this.mempoolViewer,
|
|
91
|
-
blockRewardViewer: this.params.blockRewardViewer,
|
|
92
|
-
rewardAddress: asAddress(this.config.producer.rewardAddress ?? ZERO_ADDRESS, () => `Producer config must have a valid reward address configured [${this.params.config.producer.rewardAddress}]`),
|
|
93
|
-
time: this.params.viewers.time
|
|
94
|
-
};
|
|
95
|
-
this._producer = await SimpleBlockRunner.create(params);
|
|
89
|
+
this._accountBalanceViewer = await this.locator.getInstance(AccountBalanceViewerMoniker);
|
|
90
|
+
this._blockRunner = await this.locator.getInstance(BlockRunnerMoniker);
|
|
91
|
+
this._blockViewer = await this.locator.getInstance(BlockViewerMoniker);
|
|
92
|
+
this._mempoolRunner = await this.locator.getInstance(MempoolRunnerMoniker);
|
|
93
|
+
this._mempoolViewer = await this.locator.getInstance(MempoolViewerMoniker);
|
|
94
|
+
this._stakeTotalsViewer = await this.locator.getInstance(StakeTotalsViewerMoniker);
|
|
96
95
|
}
|
|
97
96
|
async startHandler() {
|
|
98
97
|
await super.startHandler();
|
|
@@ -111,21 +110,9 @@ var ProducerActor = class extends Actor {
|
|
|
111
110
|
}, TEN_MINUTES, TEN_MINUTES);
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// // TODO: This doesn't handle the case where the producer had declared a range for the future
|
|
118
|
-
// // but we're in a range that's not the future
|
|
119
|
-
// // Sort in ascending order based on ending range to get range with highest ending block
|
|
120
|
-
// const lastRange = ranges.toSorted((a, b) => a[1] > b[1] ? 1 : -1).at(-1)
|
|
121
|
-
// // Use the most recent range's end block as the current declaration end OR
|
|
122
|
-
// const [, currentDeclarationEnd] = lastRange
|
|
123
|
-
// // If we have no ranges, we need to declare intent, so use the current block
|
|
124
|
-
// ?? [undefined, currentBlock]
|
|
125
|
-
// // Calculate the time until the producer's declaration expires
|
|
126
|
-
// const timeToProducerExpiration = currentDeclarationEnd - currentBlock
|
|
127
|
-
// return timeToProducerExpiration
|
|
128
|
-
// }
|
|
113
|
+
calculateBlocksUntilProducerDeclarationExpiration(currentBlock) {
|
|
114
|
+
return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock;
|
|
115
|
+
}
|
|
129
116
|
async produceBlock() {
|
|
130
117
|
this._producerActorBlockProductionChecks?.add(1, this._metricAttributes);
|
|
131
118
|
await this.spanAsync("produceBlock", async () => {
|
|
@@ -134,19 +121,13 @@ var ProducerActor = class extends Actor {
|
|
|
134
121
|
return;
|
|
135
122
|
}
|
|
136
123
|
await this._produceBlockMutex.runExclusive(async () => {
|
|
137
|
-
const headStart = Date.now();
|
|
138
124
|
const head = (await this.blockViewer.currentBlock())[0];
|
|
139
|
-
const headDuration = Date.now() - headStart;
|
|
140
|
-
if (headDuration > 500) this.logger?.warn(`[Slow] Fetched head in ${headDuration}ms: 0x${toHex(head._hash)}`);
|
|
141
125
|
const headHash = head._hash;
|
|
142
126
|
if (this._lastProducedBlock && this._lastProducedBlock[0].previous === headHash) {
|
|
143
127
|
this.logger?.log("Block already produced:", `0x${toHex(this._lastProducedBlock[0].block)}`, this._lastProducedBlock[0].block);
|
|
144
128
|
} else {
|
|
145
129
|
this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes);
|
|
146
|
-
const
|
|
147
|
-
const nextBlock = await this.producer.next(head);
|
|
148
|
-
const nextDuration = Date.now() - nextStart;
|
|
149
|
-
if (nextDuration > 1e3) this.logger?.warn(`[Slow] Generated next block in ${nextDuration}ms, block: ${nextBlock?.[0]._hash}`);
|
|
130
|
+
const nextBlock = await this.blockRunner.next(head);
|
|
150
131
|
if (nextBlock) {
|
|
151
132
|
const displayBlockNumber = `0x${toHex(nextBlock[0].block)}`;
|
|
152
133
|
this.logger?.log("Produced block:", displayBlockNumber);
|
|
@@ -166,10 +147,15 @@ var ProducerActor = class extends Actor {
|
|
|
166
147
|
}
|
|
167
148
|
async redeclareIntent() {
|
|
168
149
|
await this.spanAsync("redeclareIntent", async () => {
|
|
169
|
-
if (this.
|
|
150
|
+
if (this.config.producer.disableIntentRedeclaration) return;
|
|
170
151
|
const head = (await this.blockViewer.currentBlock())[0];
|
|
171
152
|
if (isUndefined(head)) return;
|
|
172
153
|
const currentBlock = head.block;
|
|
154
|
+
const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock);
|
|
155
|
+
if (blocksUntilExpiration > _ProducerActor.RedeclarationWindow * 0.1) {
|
|
156
|
+
this._lastRedeclarationIntent = void 0;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
173
159
|
if (this._lastRedeclarationIntent) {
|
|
174
160
|
if (this._lastRedeclarationIntent.exp > currentBlock) return;
|
|
175
161
|
this._lastRedeclarationIntent = void 0;
|
|
@@ -179,7 +165,7 @@ var ProducerActor = class extends Actor {
|
|
|
179
165
|
return;
|
|
180
166
|
}
|
|
181
167
|
if (!await this.validateCurrentStake()) {
|
|
182
|
-
this.logger?.error(`Add stake to contract address ${this.
|
|
168
|
+
this.logger?.error(`Add stake to contract address ${this.config.chain.id} for the producer to declare it's intent.`);
|
|
183
169
|
return;
|
|
184
170
|
}
|
|
185
171
|
this.logger?.log("Creating redeclaration intent for producer:", this.account.address);
|
|
@@ -203,7 +189,9 @@ var ProducerActor = class extends Actor {
|
|
|
203
189
|
if (isDefined(head)) {
|
|
204
190
|
const balances = await this.accountBalanceViewer.accountBalances([
|
|
205
191
|
this.account.address
|
|
206
|
-
],
|
|
192
|
+
], {
|
|
193
|
+
head
|
|
194
|
+
});
|
|
207
195
|
const currentBalance = balances[this.account.address] ?? 0n;
|
|
208
196
|
if (currentBalance <= 0n) {
|
|
209
197
|
this.logger?.error(`Producer ${this.account.address} has no balance.`);
|
|
@@ -228,87 +216,52 @@ ProducerActor = _ts_decorate([
|
|
|
228
216
|
], ProducerActor);
|
|
229
217
|
|
|
230
218
|
// src/runProducer.ts
|
|
231
|
-
import { assertEx, exists } from "@xylabs/sdk-js";
|
|
232
|
-
import { initProducerAccount } from "@xyo-network/chain-orchestration";
|
|
233
|
-
import {
|
|
234
|
-
import {
|
|
235
|
-
import {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
219
|
+
import { asAddress, assertEx, exists } from "@xylabs/sdk-js";
|
|
220
|
+
import { initProducerAccount as initProducerAccount2 } from "@xyo-network/chain-orchestration";
|
|
221
|
+
import { validateHydratedBlock, validateHydratedBlockState } from "@xyo-network/chain-validation";
|
|
222
|
+
import { SimpleBlockRunner as SimpleBlockRunner2 } from "@xyo-network/chain-services";
|
|
223
|
+
import { SimpleBlockRewardViewer, SimpleBlockValidationViewer } from "@xyo-network/xl1-protocol-sdk";
|
|
224
|
+
import { buildJsonRpcProviderLocator } from "@xyo-network/xl1-providers";
|
|
225
|
+
import { HttpRpcTransport } from "@xyo-network/xl1-rpc";
|
|
226
|
+
var getProviderFactoryLocator = /* @__PURE__ */ __name(async (context, account, rewardAddress) => {
|
|
227
|
+
const endpoint = assertEx(context.config.services.apiEndpoint, () => "API endpoint is required in config.services.apiEndpoint");
|
|
228
|
+
const transportFactory = /* @__PURE__ */ __name((schemas) => new HttpRpcTransport(endpoint, schemas), "transportFactory");
|
|
229
|
+
const locator = await buildJsonRpcProviderLocator({
|
|
230
|
+
context,
|
|
231
|
+
transportFactory
|
|
242
232
|
});
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
...BlockViewerRpcSchemas
|
|
233
|
+
locator.register(SimpleBlockRewardViewer.factory());
|
|
234
|
+
locator.register(SimpleBlockValidationViewer.factory({
|
|
235
|
+
state: validateHydratedBlockState,
|
|
236
|
+
value: validateHydratedBlock
|
|
248
237
|
}));
|
|
249
|
-
|
|
250
|
-
...StakeTotalsViewerRpcSchemas
|
|
251
|
-
}));
|
|
252
|
-
const chainId = (await blockViewer.currentBlock())[0].chain;
|
|
253
|
-
const mempoolViewer = new JsonRpcMempoolViewer(new HttpRpcTransport(endpoint, {
|
|
254
|
-
...MempoolViewerRpcSchemas
|
|
255
|
-
}));
|
|
256
|
-
const mempoolRunner = new JsonRpcMempoolRunner(new HttpRpcTransport(endpoint, {
|
|
257
|
-
...MempoolRunnerRpcSchemas
|
|
258
|
-
}));
|
|
259
|
-
const chainService = await initChainService({
|
|
238
|
+
locator.register(SimpleBlockRunner2.factory({
|
|
260
239
|
account,
|
|
261
|
-
|
|
262
|
-
|
|
240
|
+
rewardAddress
|
|
241
|
+
}));
|
|
242
|
+
return locator;
|
|
243
|
+
}, "getProviderFactoryLocator");
|
|
244
|
+
var runProducer = /* @__PURE__ */ __name(async ({ config, logger, orchestrator }) => {
|
|
245
|
+
const account = await initProducerAccount2({
|
|
246
|
+
config
|
|
263
247
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
forkedAtHash: /* @__PURE__ */ __name(function() {
|
|
269
|
-
return chainService.forkedAtHash();
|
|
270
|
-
}, "forkedAtHash"),
|
|
271
|
-
forkedChainId: /* @__PURE__ */ __name(function() {
|
|
272
|
-
return chainService.forkedChainId();
|
|
273
|
-
}, "forkedChainId"),
|
|
274
|
-
minWithdrawalBlocks: /* @__PURE__ */ __name(function() {
|
|
275
|
-
return chainService.minWithdrawalBlocks();
|
|
276
|
-
}, "minWithdrawalBlocks"),
|
|
277
|
-
rewardsContract: /* @__PURE__ */ __name(function() {
|
|
278
|
-
return chainService.rewardsContract();
|
|
279
|
-
}, "rewardsContract"),
|
|
280
|
-
stakingTokenAddress: /* @__PURE__ */ __name(function() {
|
|
281
|
-
return chainService.stakingTokenAddress();
|
|
282
|
-
}, "stakingTokenAddress"),
|
|
283
|
-
moniker: ChainContractViewerMoniker
|
|
284
|
-
};
|
|
285
|
-
const blockRewardViewer = await initBlockRewardViewer({
|
|
248
|
+
logger.info(`Running producer for account ${account.address}`);
|
|
249
|
+
const rewardAddress = asAddress(config.producer.rewardAddress, () => "rewardAddress is required in config.producer");
|
|
250
|
+
logger.info(`Using reward address ${rewardAddress}`);
|
|
251
|
+
const locator = await getProviderFactoryLocator({
|
|
286
252
|
config,
|
|
287
253
|
logger,
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
const
|
|
254
|
+
singletons: {}
|
|
255
|
+
}, account, rewardAddress);
|
|
256
|
+
const context = {
|
|
257
|
+
locator,
|
|
291
258
|
config,
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
259
|
+
singletons: locator.context.singletons
|
|
260
|
+
};
|
|
295
261
|
const params = {
|
|
296
|
-
account,
|
|
297
262
|
id: "ProducerActor",
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
viewers: {
|
|
301
|
-
accountBalanceViewer,
|
|
302
|
-
blockViewer,
|
|
303
|
-
stakeTotalsViewer,
|
|
304
|
-
mempoolViewer,
|
|
305
|
-
time: timeSyncViewer
|
|
306
|
-
},
|
|
307
|
-
blockRewardViewer,
|
|
308
|
-
runners: {
|
|
309
|
-
mempoolRunner
|
|
310
|
-
},
|
|
311
|
-
logger
|
|
263
|
+
context,
|
|
264
|
+
account
|
|
312
265
|
};
|
|
313
266
|
const producer = await ProducerActor.create(params);
|
|
314
267
|
const actors = [
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ProducerActor.ts","../../src/runProducer.ts"],"sourcesContent":["import type { Attributes, Counter } from '@opentelemetry/api'\nimport type { CreatableName } from '@xylabs/sdk-js'\nimport {\n asAddress,\n creatable, isDefined, isUndefined, toHex,\n ZERO_ADDRESS,\n} from '@xylabs/sdk-js'\nimport { AccountInstance } from '@xyo-network/account-model'\nimport { Actor, type ActorParams } from '@xyo-network/chain-orchestration'\nimport { createDeclarationIntent } from '@xyo-network/chain-protocol'\nimport { SimpleBlockRunner, SimpleBlockRunnerParams } from '@xyo-network/chain-services'\nimport type {\n ChainId,\n ChainStakeIntent, HydratedBlockWithHashMeta,\n XL1BlockNumber,\n} from '@xyo-network/xl1-protocol'\nimport { asXL1BlockNumber } from '@xyo-network/xl1-protocol'\nimport {\n AccountBalanceViewer,\n BlockProducerService, BlockRewardViewer, BlockViewer, buildTransaction,\n Config,\n MempoolRunner,\n MempoolViewer,\n StakeTotalsViewer,\n TimeSyncViewer,\n} from '@xyo-network/xl1-protocol-sdk'\nimport { Mutex } from 'async-mutex'\n\nexport type ProducerActorParams = ActorParams<{\n account: AccountInstance\n blockRewardViewer: BlockRewardViewer\n chainId: ChainId\n config: Config\n runners: {\n mempoolRunner: MempoolRunner\n }\n viewers: {\n accountBalanceViewer: AccountBalanceViewer\n blockViewer: BlockViewer\n mempoolViewer: MempoolViewer\n stakeTotalsViewer: StakeTotalsViewer\n time: TimeSyncViewer\n }\n}>\n\nconst SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true\nconst TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds\n\n@creatable()\nexport class ProducerActor extends Actor<ProducerActorParams> {\n protected _lastProducedBlock: HydratedBlockWithHashMeta | undefined\n protected _lastRedeclarationIntent: ChainStakeIntent | undefined\n protected _metricAttributes: Attributes | undefined\n protected _producerActorBlockProductionAttempts: Counter<Attributes> | undefined\n protected _producerActorBlockProductionChecks: Counter<Attributes> | undefined\n protected _producerActorBlocksProduced: Counter<Attributes> | undefined\n protected _producerActorBlocksPublished: Counter<Attributes> | undefined\n\n private _produceBlockMutex = new Mutex()\n private _producer: BlockProducerService | undefined\n\n protected get account() {\n return this.params.account!\n }\n\n protected get accountBalanceViewer() {\n return this.params.viewers.accountBalanceViewer!\n }\n\n protected get blockViewer() {\n return this.params.viewers.blockViewer!\n }\n\n protected get chainId() {\n return this.params.chainId!\n }\n\n protected get config() {\n return this.params.config!\n }\n\n protected get mempoolRunner() {\n return this.params.runners.mempoolRunner!\n }\n\n protected get mempoolViewer() {\n return this.params.viewers.mempoolViewer!\n }\n\n protected get producer() {\n return this._producer!\n }\n\n protected get stakeTotalsViewer() {\n return this.params.viewers.stakeTotalsViewer!\n }\n\n static override async paramsHandler<T extends ActorParams>(params?: Partial<T>) {\n return await super.paramsHandler({ ...params, name: (params?.name ?? 'Producer') as CreatableName })\n }\n\n override async createHandler() {\n await super.createHandler()\n // Create the consistent meter attributes that will\n // be included with all metrics from this actor\n this._metricAttributes = { address: this.account.address.toString() }\n // Create the metrics\n this._producerActorBlockProductionChecks = this.meter?.createCounter(\n 'producer_actor_block_production_checks',\n { description: 'Number of block production checks' },\n )\n this._producerActorBlockProductionAttempts = this.meter?.createCounter(\n 'producer_actor_block_production_attempts',\n { description: 'Number of block production attempts' },\n )\n this._producerActorBlocksProduced = this.meter?.createCounter(\n 'producer_actor_blocks_produced',\n { description: 'Number of blocks produced' },\n )\n this._producerActorBlocksPublished = this.meter?.createCounter(\n 'producer_actor_blocks_published',\n { description: 'Number of blocks published' },\n )\n const params = {\n account: this.account,\n balanceViewer: this.accountBalanceViewer,\n config: this.config,\n chainId: this.chainId,\n mempoolRunner: this.mempoolRunner,\n mempoolViewer: this.mempoolViewer,\n blockRewardViewer: this.params.blockRewardViewer!,\n rewardAddress: asAddress(\n this.config.producer.rewardAddress ?? ZERO_ADDRESS,\n () => `Producer config must have a valid reward address configured [${this.params.config.producer.rewardAddress}]`,\n ),\n time: this.params.viewers.time!,\n } satisfies SimpleBlockRunnerParams\n this._producer = await SimpleBlockRunner.create(params)\n }\n\n override async startHandler() {\n await super.startHandler()\n // Register a timer to check if we should produce a block\n this.registerTimer('BlockProductionTimer', async () => {\n await this.produceBlock()\n }, 2000, 1500/* 500 */)\n\n if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {\n // Register a timer to check if we should redeclare the producer\n this.registerTimer('ProducerRedeclarationTimer', async () => {\n await this.redeclareIntent()\n }, TEN_MINUTES, TEN_MINUTES)\n }\n }\n\n // protected async calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): Promise<number> {\n // // Decide if we need to redeclare intent\n // const ranges = await this.stakeIntentService.getDeclaredCandidateRanges(this.account.address, 'producer')\n // // TODO: This doesn't handle the case where the producer had declared a range for the future\n // // but we're in a range that's not the future\n // // Sort in ascending order based on ending range to get range with highest ending block\n // const lastRange = ranges.toSorted((a, b) => a[1] > b[1] ? 1 : -1).at(-1)\n\n // // Use the most recent range's end block as the current declaration end OR\n // const [, currentDeclarationEnd] = lastRange\n // // If we have no ranges, we need to declare intent, so use the current block\n // ?? [undefined, currentBlock]\n\n // // Calculate the time until the producer's declaration expires\n // const timeToProducerExpiration = currentDeclarationEnd - currentBlock\n // return timeToProducerExpiration\n // }\n\n protected async produceBlock(): Promise<void> {\n this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)\n await this.spanAsync('produceBlock', async () => {\n if (this._produceBlockMutex.isLocked()) {\n this.logger?.log('Skipping block production, previous production still in progress')\n return\n }\n\n await this._produceBlockMutex.runExclusive(async () => {\n // Get the updated head\n const headStart = Date.now()\n const head = (await this.blockViewer.currentBlock())[0]\n const headDuration = Date.now() - headStart\n if (headDuration > 500) this.logger?.warn(`[Slow] Fetched head in ${headDuration}ms: 0x${toHex(head._hash)}`)\n // Check if we've already produced the next block for this head\n const headHash = head._hash\n // If our last produced block was the next block for the current head, we do not\n // need to produce another. This prevents duplicate blocks from being produced\n if (this._lastProducedBlock && this._lastProducedBlock[0].previous === headHash) {\n this.logger?.log('Block already produced:', `0x${toHex(this._lastProducedBlock[0].block)}`, this._lastProducedBlock[0].block)\n } else {\n this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)\n // Produce the next block\n const nextStart = Date.now()\n const nextBlock = await this.producer.next(head)\n const nextDuration = Date.now() - nextStart\n if (nextDuration > 1000) this.logger?.warn(`[Slow] Generated next block in ${nextDuration}ms, block: ${nextBlock?.[0]._hash}`)\n // If it was produced\n if (nextBlock) {\n const displayBlockNumber = `0x${toHex(nextBlock[0].block)}`\n this.logger?.log('Produced block:', displayBlockNumber)\n this._producerActorBlocksProduced?.add(1, this._metricAttributes)\n // Insert the block into the chain\n await this.mempoolRunner.submitBlocks([nextBlock])\n this.logger?.log('Published block:', displayBlockNumber, nextBlock[0].block)\n this._producerActorBlocksPublished?.add(1, this._metricAttributes)\n // Record that we have produced a block so we do not produce it again\n this._lastProducedBlock = nextBlock\n } else {\n this.logger?.log('No block produced at this time.')\n }\n }\n })\n })\n }\n\n protected async redeclareIntent(): Promise<void> {\n await this.spanAsync('redeclareIntent', async () => {\n // Decide if we should redeclare intent\n if (this.params.config.producer.disableIntentRedeclaration) return\n\n // Get the current block\n const head = (await this.blockViewer.currentBlock())[0]\n if (isUndefined(head)) return\n const currentBlock = head.block\n\n // // Calculate the time until the producer's declaration expires\n // const blocksUntilExpiration = await this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)\n\n // // Allow the producer time to redeclare itself via block production\n // // (for free) before submitting a redeclaration intent transaction.\n // if (blocksUntilExpiration > BaseBlockProducerService.RedeclarationWindow * 0.1) {\n // // Clear any previous redeclaration intent\n // this._lastRedeclarationIntent = undefined\n // // No need to redeclare yet\n // return\n // }\n\n // If we already have a valid redeclaration intent, do not create another\n // unless it has expired.\n if (this._lastRedeclarationIntent) {\n // Check if the last redeclaration intent is still valid\n if (this._lastRedeclarationIntent.exp > currentBlock) return\n // If it has expired, clear the last redeclaration intent\n this._lastRedeclarationIntent = undefined\n }\n\n // Check if we have a valid balance before declaring intent\n if (!await this.validateCurrentBalance()) {\n this.logger?.error(\n `Add balance to address ${this.account.address} for the producer to declare it's intent.`,\n )\n return\n }\n\n // Check if we have a valid stake before declaring intent\n if (!(await this.validateCurrentStake())) {\n this.logger?.error(\n `Add stake to contract address ${this.params.config.chain.id}`\n + ' for the producer to declare it\\'s intent.',\n )\n return\n }\n\n // Create a redeclaration intent\n this.logger?.log('Creating redeclaration intent for producer:', this.account.address)\n const redeclarationIntent = createDeclarationIntent(\n this.account.address,\n 'producer',\n currentBlock,\n currentBlock + SimpleBlockRunner.RedeclarationDuration,\n )\n\n // Submit the redeclaration intent\n await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)\n\n // On successful submission, save the redeclaration intent\n this._lastRedeclarationIntent = redeclarationIntent\n })\n }\n\n protected async submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void> {\n this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)\n // Create a transaction to submit the redeclaration intent\n const tx = await buildTransaction(\n this.chainId,\n [redeclarationIntent],\n [],\n this.account,\n currentBlock,\n asXL1BlockNumber(currentBlock + 1000, true),\n )\n\n // Submit the redeclaration intent\n await this.mempoolRunner.submitTransactions([tx])\n\n this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)\n }\n\n protected async validateCurrentBalance(): Promise<boolean> {\n // Check if we have a valid balance before declaring intent\n const head = this._lastProducedBlock?.[0]._hash\n if (isDefined(head)) {\n const balances = await this.accountBalanceViewer.accountBalances([this.account.address], head)\n const currentBalance = balances[this.account.address] ?? 0n\n if (currentBalance <= 0n) {\n this.logger?.error(`Producer ${this.account.address} has no balance.`)\n return false\n }\n return true\n }\n return true\n }\n\n protected async validateCurrentStake(): Promise<boolean> {\n // Use StakeIntentService to get the required minimum stake\n const requiredMinimumStake = 1n // this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')\n // Check if we have a valid stake before declaring intent\n const currentStake = await this.stakeTotalsViewer.activeByStaked(this.account.address)\n if (currentStake < requiredMinimumStake) {\n this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)\n return false\n }\n return true\n }\n}\n","import type { Address, Logger } from '@xylabs/sdk-js'\nimport { assertEx, exists } from '@xylabs/sdk-js'\nimport { initProducerAccount, type OrchestratorInstance } from '@xyo-network/chain-orchestration'\nimport {\n initBlockRewardViewer, initChainService, initTimeService,\n} from '@xyo-network/chain-services'\nimport {\n type ChainContractViewer, ChainContractViewerMoniker, type Config,\n} from '@xyo-network/xl1-protocol-sdk'\nimport {\n AccountBalanceViewerRpcSchemas, BlockViewerRpcSchemas, HttpRpcTransport, JsonRpcAccountBalanceViewer,\n JsonRpcBlockViewer,\n JsonRpcMempoolRunner,\n JsonRpcMempoolViewer,\n JsonRpcStakeTotalsViewer,\n MempoolRunnerRpcSchemas,\n MempoolViewerRpcSchemas,\n StakeTotalsViewerRpcSchemas,\n} from '@xyo-network/xl1-rpc'\n\nimport { ProducerActor, type ProducerActorParams } from './ProducerActor.ts'\n\ninterface RunProducerContext {\n config: Config\n logger: Logger\n orchestrator: OrchestratorInstance\n}\n\nexport const runProducer = async (context: RunProducerContext) => {\n const {\n config, logger, orchestrator,\n } = context\n\n const endpoint = assertEx(config.services?.apiEndpoint, () => 'No API endpoint configured')\n const account = await initProducerAccount({ config, logger })\n const accountBalanceViewer = new JsonRpcAccountBalanceViewer(new HttpRpcTransport(endpoint, { ...AccountBalanceViewerRpcSchemas }))\n const blockViewer = new JsonRpcBlockViewer(new HttpRpcTransport(endpoint, { ...BlockViewerRpcSchemas }))\n const stakeTotalsViewer = new JsonRpcStakeTotalsViewer(new HttpRpcTransport(endpoint, { ...StakeTotalsViewerRpcSchemas }))\n const chainId = (await blockViewer.currentBlock())[0].chain\n const mempoolViewer = new JsonRpcMempoolViewer(new HttpRpcTransport(endpoint, { ...MempoolViewerRpcSchemas }))\n const mempoolRunner = new JsonRpcMempoolRunner(new HttpRpcTransport(endpoint, { ...MempoolRunnerRpcSchemas }))\n const chainService = await initChainService({\n account, config, logger,\n })\n const chainContractViewer = {\n forkedAtBlockNumber: function (): Promise<bigint> {\n return chainService.forkedAtBlockNumber()\n },\n forkedAtHash: function (): Promise<bigint> {\n return chainService.forkedAtHash()\n },\n forkedChainId: function (): Promise<Address> {\n return chainService.forkedChainId()\n },\n minWithdrawalBlocks: function (): Promise<bigint> {\n return chainService.minWithdrawalBlocks()\n },\n rewardsContract: function (): Promise<string> {\n return chainService.rewardsContract()\n },\n stakingTokenAddress: function (): Promise<string> {\n return chainService.stakingTokenAddress()\n },\n moniker: ChainContractViewerMoniker,\n } satisfies ChainContractViewer\n const blockRewardViewer = await initBlockRewardViewer({\n config, logger, chainContractViewer,\n })\n const timeSyncViewer = await initTimeService({\n config, logger, blockViewer,\n })\n\n // Create actors\n const params = {\n account,\n id: 'ProducerActor',\n chainId,\n config,\n viewers: {\n accountBalanceViewer,\n blockViewer,\n stakeTotalsViewer,\n mempoolViewer,\n time: timeSyncViewer,\n },\n blockRewardViewer,\n runners: { mempoolRunner },\n logger,\n } satisfies ProducerActorParams\n // const apiEndpoint = config.services.apiEndpoint\n // const balances = isDefined(apiEndpoint) ? undefined : await BalanceActor.create(params)\n const producer = await ProducerActor.create(params)\n const actors = [producer].filter(exists)\n\n for (const actor of actors) {\n // Register the actor with the orchestrator\n await orchestrator.registerActor(actor)\n }\n // Start the orchestrator => automatically activates the actor\n await orchestrator.start()\n}\n"],"mappings":"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","names":["asAddress","creatable","isDefined","isUndefined","toHex","ZERO_ADDRESS","Actor","createDeclarationIntent","SimpleBlockRunner","asXL1BlockNumber","buildTransaction","Mutex","SHOULD_REGISTER_REDECLARATION_INTENT_TIMER","TEN_MINUTES","ProducerActor","Actor","_lastProducedBlock","_lastRedeclarationIntent","_metricAttributes","_producerActorBlockProductionAttempts","_producerActorBlockProductionChecks","_producerActorBlocksProduced","_producerActorBlocksPublished","_produceBlockMutex","Mutex","_producer","account","params","accountBalanceViewer","viewers","blockViewer","chainId","config","mempoolRunner","runners","mempoolViewer","producer","stakeTotalsViewer","paramsHandler","name","createHandler","address","toString","meter","createCounter","description","balanceViewer","blockRewardViewer","rewardAddress","asAddress","ZERO_ADDRESS","time","SimpleBlockRunner","create","startHandler","registerTimer","produceBlock","redeclareIntent","add","spanAsync","isLocked","logger","log","runExclusive","headStart","Date","now","head","currentBlock","headDuration","warn","toHex","_hash","headHash","previous","block","nextStart","nextBlock","next","nextDuration","displayBlockNumber","submitBlocks","disableIntentRedeclaration","isUndefined","exp","undefined","validateCurrentBalance","error","validateCurrentStake","chain","id","redeclarationIntent","createDeclarationIntent","RedeclarationDuration","submitRedeclarationIntent","tx","buildTransaction","asXL1BlockNumber","submitTransactions","isDefined","balances","accountBalances","currentBalance","requiredMinimumStake","currentStake","activeByStaked","assertEx","exists","initProducerAccount","initBlockRewardViewer","initChainService","initTimeService","ChainContractViewerMoniker","AccountBalanceViewerRpcSchemas","BlockViewerRpcSchemas","HttpRpcTransport","JsonRpcAccountBalanceViewer","JsonRpcBlockViewer","JsonRpcMempoolRunner","JsonRpcMempoolViewer","JsonRpcStakeTotalsViewer","MempoolRunnerRpcSchemas","MempoolViewerRpcSchemas","StakeTotalsViewerRpcSchemas","runProducer","context","config","logger","orchestrator","endpoint","assertEx","services","apiEndpoint","account","initProducerAccount","accountBalanceViewer","JsonRpcAccountBalanceViewer","HttpRpcTransport","AccountBalanceViewerRpcSchemas","blockViewer","JsonRpcBlockViewer","BlockViewerRpcSchemas","stakeTotalsViewer","JsonRpcStakeTotalsViewer","StakeTotalsViewerRpcSchemas","chainId","currentBlock","chain","mempoolViewer","JsonRpcMempoolViewer","MempoolViewerRpcSchemas","mempoolRunner","JsonRpcMempoolRunner","MempoolRunnerRpcSchemas","chainService","initChainService","chainContractViewer","forkedAtBlockNumber","forkedAtHash","forkedChainId","minWithdrawalBlocks","rewardsContract","stakingTokenAddress","moniker","ChainContractViewerMoniker","blockRewardViewer","initBlockRewardViewer","timeSyncViewer","initTimeService","params","id","viewers","time","runners","producer","ProducerActor","create","actors","filter","exists","actor","registerActor","start"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ProducerActor.ts","../../src/runProducer.ts"],"sourcesContent":["import type { Attributes, Counter } from '@opentelemetry/api'\nimport type { CreatableName } from '@xylabs/sdk-js'\nimport {\n creatable, isDefined, isUndefined, toHex,\n} from '@xylabs/sdk-js'\nimport {\n Actor, type ActorParams, initProducerAccount,\n} from '@xyo-network/chain-orchestration'\nimport { SimpleBlockRunner } from '@xyo-network/chain-services'\nimport type {\n ChainId,\n ChainStakeIntent, HydratedBlockWithHashMeta,\n XL1BlockNumber,\n} from '@xyo-network/xl1-protocol'\nimport { asXL1BlockNumber } from '@xyo-network/xl1-protocol'\nimport {\n AccountBalanceViewer, AccountBalanceViewerMoniker, BlockRunner, BlockRunnerMoniker,\n BlockViewer, BlockViewerMoniker, buildTransaction,\n Config,\n createDeclarationIntent,\n getDefaultConfig,\n MempoolRunner,\n MempoolRunnerMoniker,\n MempoolViewer,\n MempoolViewerMoniker,\n StakeTotalsViewer,\n StakeTotalsViewerMoniker,\n} from '@xyo-network/xl1-protocol-sdk'\nimport { Mutex } from 'async-mutex'\n\nexport type ProducerActorParams = ActorParams\n\nconst SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true\nconst TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds\n\n@creatable()\nexport class ProducerActor extends Actor<ProducerActorParams> {\n static readonly RedeclarationWindow = 10_000\n\n protected _lastProducedBlock?: HydratedBlockWithHashMeta\n protected _lastRedeclarationIntent?: ChainStakeIntent\n protected _metricAttributes?: Attributes\n protected _producerActorBlockProductionAttempts?: Counter<Attributes>\n protected _producerActorBlockProductionChecks?: Counter<Attributes>\n protected _producerActorBlocksProduced?: Counter<Attributes>\n protected _producerActorBlocksPublished?: Counter<Attributes>\n\n private _accountBalanceViewer?: AccountBalanceViewer\n private _blockRunner?: BlockRunner\n private _blockViewer?: BlockViewer\n private _chainId?: ChainId\n private _mempoolRunner?: MempoolRunner\n private _mempoolViewer?: MempoolViewer\n\n private _produceBlockMutex = new Mutex()\n\n private _stakeTotalsViewer?: StakeTotalsViewer\n\n protected get accountBalanceViewer() {\n return this._accountBalanceViewer!\n }\n\n protected get blockRunner() {\n return this._blockRunner!\n }\n\n protected get blockViewer() {\n return this._blockViewer!\n }\n\n protected get chainId() {\n return this._chainId!\n }\n\n protected get mempoolRunner() {\n return this._mempoolRunner!\n }\n\n protected get mempoolViewer() {\n return this._mempoolViewer!\n }\n\n protected get stakeTotalsViewer() {\n return this._stakeTotalsViewer!\n }\n\n static override async paramsHandler<T extends ActorParams>(params?: Partial<T>) {\n const config: Config = params?.context?.config ?? getDefaultConfig()\n const account = params?.account ?? await initProducerAccount({ config })\n return await super.paramsHandler({\n ...params, name: (params?.name ?? 'Producer') as CreatableName, account,\n })\n }\n\n override async createHandler() {\n await super.createHandler()\n // Create the consistent meter attributes that will\n // be included with all metrics from this actor\n this._metricAttributes = { address: this.account.address.toString() }\n // Create the metrics\n this._producerActorBlockProductionChecks = this.meter?.createCounter(\n 'producer_actor_block_production_checks',\n { description: 'Number of block production checks' },\n )\n this._producerActorBlockProductionAttempts = this.meter?.createCounter(\n 'producer_actor_block_production_attempts',\n { description: 'Number of block production attempts' },\n )\n this._producerActorBlocksProduced = this.meter?.createCounter(\n 'producer_actor_blocks_produced',\n { description: 'Number of blocks produced' },\n )\n this._producerActorBlocksPublished = this.meter?.createCounter(\n 'producer_actor_blocks_published',\n { description: 'Number of blocks published' },\n )\n this._accountBalanceViewer = await this.locator.getInstance<AccountBalanceViewer>(AccountBalanceViewerMoniker)\n this._blockRunner = await this.locator.getInstance<BlockRunner>(BlockRunnerMoniker)\n this._blockViewer = await this.locator.getInstance<BlockViewer>(BlockViewerMoniker)\n this._mempoolRunner = await this.locator.getInstance<MempoolRunner>(MempoolRunnerMoniker)\n this._mempoolViewer = await this.locator.getInstance<MempoolViewer>(MempoolViewerMoniker)\n this._stakeTotalsViewer = await this.locator.getInstance<StakeTotalsViewer>(StakeTotalsViewerMoniker)\n }\n\n override async startHandler() {\n await super.startHandler()\n // Register a timer to check if we should produce a block\n this.registerTimer('BlockProductionTimer', async () => {\n await this.produceBlock()\n }, 2000, 1500/* 500 */)\n\n if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {\n // Register a timer to check if we should redeclare the producer\n this.registerTimer('ProducerRedeclarationTimer', async () => {\n await this.redeclareIntent()\n }, TEN_MINUTES, TEN_MINUTES)\n }\n }\n\n protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number {\n return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock\n }\n\n protected async produceBlock(): Promise<void> {\n this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)\n await this.spanAsync('produceBlock', async () => {\n if (this._produceBlockMutex.isLocked()) {\n this.logger?.log('Skipping block production, previous production still in progress')\n return\n }\n\n await this._produceBlockMutex.runExclusive(async () => {\n // Get the updated head\n const head = (await this.blockViewer.currentBlock())[0]\n // Check if we've already produced the next block for this head\n const headHash = head._hash\n // If our last produced block was the next block for the current head, we do not\n // need to produce another. This prevents duplicate blocks from being produced\n if (this._lastProducedBlock && this._lastProducedBlock[0].previous === headHash) {\n this.logger?.log('Block already produced:', `0x${toHex(this._lastProducedBlock[0].block)}`, this._lastProducedBlock[0].block)\n } else {\n this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)\n // Produce the next block\n const nextBlock = await this.blockRunner.next(head)\n // If it was produced\n if (nextBlock) {\n const displayBlockNumber = `0x${toHex(nextBlock[0].block)}`\n this.logger?.log('Produced block:', displayBlockNumber)\n this._producerActorBlocksProduced?.add(1, this._metricAttributes)\n // Insert the block into the chain\n await this.mempoolRunner.submitBlocks([nextBlock])\n this.logger?.log('Published block:', displayBlockNumber, nextBlock[0].block)\n this._producerActorBlocksPublished?.add(1, this._metricAttributes)\n // Record that we have produced a block so we do not produce it again\n this._lastProducedBlock = nextBlock\n } else {\n this.logger?.log('No block produced at this time.')\n }\n }\n })\n })\n }\n\n protected async redeclareIntent(): Promise<void> {\n await this.spanAsync('redeclareIntent', async () => {\n // Decide if we should redeclare intent\n if (this.config.producer.disableIntentRedeclaration) return\n\n // Get the current block\n const head = (await this.blockViewer.currentBlock())[0]\n if (isUndefined(head)) return\n const currentBlock = head.block\n\n // // Calculate the time until the producer's declaration expires\n const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)\n\n // Allow the producer time to redeclare itself via block production\n // (for free) before submitting a redeclaration intent transaction.\n if (blocksUntilExpiration > ProducerActor.RedeclarationWindow * 0.1) {\n // Clear any previous redeclaration intent\n this._lastRedeclarationIntent = undefined\n // No need to redeclare yet\n return\n }\n\n // If we already have a valid redeclaration intent, do not create another\n // unless it has expired.\n if (this._lastRedeclarationIntent) {\n // Check if the last redeclaration intent is still valid\n if (this._lastRedeclarationIntent.exp > currentBlock) return\n // If it has expired, clear the last redeclaration intent\n this._lastRedeclarationIntent = undefined\n }\n\n // Check if we have a valid balance before declaring intent\n if (!await this.validateCurrentBalance()) {\n this.logger?.error(\n `Add balance to address ${this.account.address} for the producer to declare it's intent.`,\n )\n return\n }\n\n // Check if we have a valid stake before declaring intent\n if (!(await this.validateCurrentStake())) {\n this.logger?.error(\n `Add stake to contract address ${this.config.chain.id}`\n + ' for the producer to declare it\\'s intent.',\n )\n return\n }\n\n // Create a redeclaration intent\n this.logger?.log('Creating redeclaration intent for producer:', this.account.address)\n const redeclarationIntent = createDeclarationIntent(\n this.account.address,\n 'producer',\n currentBlock,\n currentBlock + SimpleBlockRunner.RedeclarationDuration,\n )\n\n // Submit the redeclaration intent\n await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)\n\n // On successful submission, save the redeclaration intent\n this._lastRedeclarationIntent = redeclarationIntent\n })\n }\n\n protected async submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void> {\n this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)\n // Create a transaction to submit the redeclaration intent\n const tx = await buildTransaction(\n this.chainId,\n [redeclarationIntent],\n [],\n this.account,\n currentBlock,\n asXL1BlockNumber(currentBlock + 1000, true),\n )\n\n // Submit the redeclaration intent\n await this.mempoolRunner.submitTransactions([tx])\n\n this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)\n }\n\n protected async validateCurrentBalance(): Promise<boolean> {\n // Check if we have a valid balance before declaring intent\n const head = this._lastProducedBlock?.[0]._hash\n if (isDefined(head)) {\n const balances = await this.accountBalanceViewer.accountBalances([this.account.address], { head })\n const currentBalance = balances[this.account.address] ?? 0n\n if (currentBalance <= 0n) {\n this.logger?.error(`Producer ${this.account.address} has no balance.`)\n return false\n }\n return true\n }\n return true\n }\n\n protected async validateCurrentStake(): Promise<boolean> {\n // Use StakeIntentService to get the required minimum stake\n const requiredMinimumStake = 1n // this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')\n // Check if we have a valid stake before declaring intent\n const currentStake = await this.stakeTotalsViewer.activeByStaked(this.account.address)\n if (currentStake < requiredMinimumStake) {\n this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)\n return false\n }\n return true\n }\n}\n","import type { Address, Logger } from '@xylabs/sdk-js'\nimport {\n asAddress, assertEx, exists,\n} from '@xylabs/sdk-js'\nimport type { AccountInstance } from '@xyo-network/account-model'\nimport {\n type ActorContext, initProducerAccount, type OrchestratorInstance,\n} from '@xyo-network/chain-orchestration'\nimport { validateHydratedBlock, validateHydratedBlockState } from '@xyo-network/chain-validation'\nimport { SimpleBlockRunner } from '@xyo-network/chain-services'\nimport type {\n Config, CreatableProviderContext, ProviderFactoryLocatorInstance,\n} from '@xyo-network/xl1-protocol-sdk'\nimport { SimpleBlockRewardViewer, SimpleBlockValidationViewer } from '@xyo-network/xl1-protocol-sdk'\nimport { buildJsonRpcProviderLocator } from '@xyo-network/xl1-providers'\nimport {\n HttpRpcTransport, type RpcSchemaMap, type TransportFactory,\n} from '@xyo-network/xl1-rpc'\n\nimport { ProducerActor, type ProducerActorParams } from './ProducerActor.ts'\n\ninterface RunProducerContext {\n config: Config\n locator?: ProviderFactoryLocatorInstance\n logger: Logger\n orchestrator: OrchestratorInstance\n}\n\nconst getProviderFactoryLocator = async (\n context: Omit<CreatableProviderContext, 'locator'>,\n account: AccountInstance,\n rewardAddress: Address,\n) => {\n const endpoint = assertEx(context.config.services.apiEndpoint, () => 'API endpoint is required in config.services.apiEndpoint')\n const transportFactory: TransportFactory = (schemas: RpcSchemaMap) => new HttpRpcTransport(endpoint, schemas)\n const locator = await buildJsonRpcProviderLocator({ context, transportFactory })\n locator.register(SimpleBlockRewardViewer.factory())\n locator.register(SimpleBlockValidationViewer.factory<SimpleBlockValidationViewer>({ state: validateHydratedBlockState, value: validateHydratedBlock }))\n locator.register(SimpleBlockRunner.factory<SimpleBlockRunner>({ account, rewardAddress }))\n return locator\n}\n\nexport const runProducer = async ({\n config, logger, orchestrator,\n}: RunProducerContext) => {\n const account = await initProducerAccount({ config })\n logger.info(`Running producer for account ${account.address}`)\n const rewardAddress = asAddress(config.producer.rewardAddress, () => 'rewardAddress is required in config.producer')\n logger.info(`Using reward address ${rewardAddress}`)\n\n const locator = await getProviderFactoryLocator({\n config, logger, singletons: {},\n }, account, rewardAddress)\n\n const context: ActorContext = {\n locator, config, singletons: locator.context.singletons,\n }\n\n // Create actors\n const params = {\n id: 'ProducerActor', context, account,\n } satisfies ProducerActorParams\n // const apiEndpoint = config.services.apiEndpoint\n // const balances = isDefined(apiEndpoint) ? undefined : await BalanceActor.create(params)\n const producer = await ProducerActor.create(params)\n const actors = [producer].filter(exists)\n\n for (const actor of actors) {\n // Register the actor with the orchestrator\n await orchestrator.registerActor(actor)\n }\n // Start the orchestrator => automatically activates the actor\n await orchestrator.start()\n}\n"],"mappings":"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","names":["creatable","isDefined","isUndefined","toHex","Actor","initProducerAccount","SimpleBlockRunner","asXL1BlockNumber","AccountBalanceViewerMoniker","BlockRunnerMoniker","BlockViewerMoniker","buildTransaction","createDeclarationIntent","getDefaultConfig","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","Mutex","SHOULD_REGISTER_REDECLARATION_INTENT_TIMER","TEN_MINUTES","ProducerActor","Actor","RedeclarationWindow","_lastProducedBlock","_lastRedeclarationIntent","_metricAttributes","_producerActorBlockProductionAttempts","_producerActorBlockProductionChecks","_producerActorBlocksProduced","_producerActorBlocksPublished","_accountBalanceViewer","_blockRunner","_blockViewer","_chainId","_mempoolRunner","_mempoolViewer","_produceBlockMutex","Mutex","_stakeTotalsViewer","accountBalanceViewer","blockRunner","blockViewer","chainId","mempoolRunner","mempoolViewer","stakeTotalsViewer","paramsHandler","params","config","context","getDefaultConfig","account","initProducerAccount","name","createHandler","address","toString","meter","createCounter","description","locator","getInstance","AccountBalanceViewerMoniker","BlockRunnerMoniker","BlockViewerMoniker","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","startHandler","registerTimer","produceBlock","redeclareIntent","calculateBlocksUntilProducerDeclarationExpiration","currentBlock","exp","add","spanAsync","isLocked","logger","log","runExclusive","head","headHash","_hash","previous","toHex","block","nextBlock","next","displayBlockNumber","submitBlocks","producer","disableIntentRedeclaration","isUndefined","blocksUntilExpiration","undefined","validateCurrentBalance","error","validateCurrentStake","chain","id","redeclarationIntent","createDeclarationIntent","SimpleBlockRunner","RedeclarationDuration","submitRedeclarationIntent","tx","buildTransaction","asXL1BlockNumber","submitTransactions","isDefined","balances","accountBalances","currentBalance","requiredMinimumStake","currentStake","activeByStaked","asAddress","assertEx","exists","initProducerAccount","validateHydratedBlock","validateHydratedBlockState","SimpleBlockRunner","SimpleBlockRewardViewer","SimpleBlockValidationViewer","buildJsonRpcProviderLocator","HttpRpcTransport","getProviderFactoryLocator","context","account","rewardAddress","endpoint","assertEx","config","services","apiEndpoint","transportFactory","schemas","HttpRpcTransport","locator","buildJsonRpcProviderLocator","register","SimpleBlockRewardViewer","factory","SimpleBlockValidationViewer","state","validateHydratedBlockState","value","validateHydratedBlock","SimpleBlockRunner","runProducer","logger","orchestrator","initProducerAccount","info","address","asAddress","producer","singletons","params","id","ProducerActor","create","actors","filter","exists","actor","registerActor","start"]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Logger } from '@xylabs/sdk-js';
|
|
2
2
|
import { type OrchestratorInstance } from '@xyo-network/chain-orchestration';
|
|
3
|
-
import {
|
|
3
|
+
import type { Config, ProviderFactoryLocatorInstance } from '@xyo-network/xl1-protocol-sdk';
|
|
4
4
|
interface RunProducerContext {
|
|
5
5
|
config: Config;
|
|
6
|
+
locator?: ProviderFactoryLocatorInstance;
|
|
6
7
|
logger: Logger;
|
|
7
8
|
orchestrator: OrchestratorInstance;
|
|
8
9
|
}
|
|
9
|
-
export declare const runProducer: (
|
|
10
|
+
export declare const runProducer: ({ config, logger, orchestrator, }: RunProducerContext) => Promise<void>;
|
|
10
11
|
export {};
|
|
11
12
|
//# sourceMappingURL=runProducer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runProducer.d.ts","sourceRoot":"","sources":["../../src/runProducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"runProducer.d.ts","sourceRoot":"","sources":["../../src/runProducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAKrD,OAAO,EACmC,KAAK,oBAAoB,EAClE,MAAM,kCAAkC,CAAA;AAGzC,OAAO,KAAK,EACV,MAAM,EAA4B,8BAA8B,EACjE,MAAM,+BAA+B,CAAA;AAStC,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,8BAA8B,CAAA;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,oBAAoB,CAAA;CACnC;AAgBD,eAAO,MAAM,WAAW,GAAU,mCAE/B,kBAAkB,kBA6BpB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyo-network/chain-producer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "XYO Layer One Producer",
|
|
5
5
|
"homepage": "https://xylabs.com",
|
|
6
6
|
"bugs": {
|
|
@@ -50,36 +50,36 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@xylabs/sdk-js": "~5.0.51",
|
|
53
|
-
"@xyo-network/chain-orchestration": "~1.
|
|
54
|
-
"@xyo-network/chain-
|
|
55
|
-
"@xyo-network/chain-services": "~1.17.7",
|
|
53
|
+
"@xyo-network/chain-orchestration": "~1.18.0",
|
|
54
|
+
"@xyo-network/chain-services": "~1.18.0",
|
|
56
55
|
"@xyo-network/xl1-protocol": "~1.14.17",
|
|
57
|
-
"@xyo-network/xl1-protocol-sdk": "~1.
|
|
58
|
-
"@xyo-network/xl1-
|
|
56
|
+
"@xyo-network/xl1-protocol-sdk": "~1.18.0",
|
|
57
|
+
"@xyo-network/xl1-providers": "~1.18.0",
|
|
58
|
+
"@xyo-network/xl1-rpc": "~1.18.0",
|
|
59
59
|
"async-mutex": "~0.5.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@opentelemetry/api": "
|
|
62
|
+
"@opentelemetry/api": "^1.9.0",
|
|
63
63
|
"@types/compression": "~1.8.1",
|
|
64
64
|
"@types/cors": "~2.8.19",
|
|
65
65
|
"@types/express": "5.0.6",
|
|
66
66
|
"@types/express-serve-static-core": "~5.1.0",
|
|
67
|
-
"@types/node": "~25.0.
|
|
67
|
+
"@types/node": "~25.0.2",
|
|
68
68
|
"@xylabs/sdk-js": "~5.0.51",
|
|
69
|
-
"@xylabs/ts-scripts-yarn3": "~7.2.
|
|
70
|
-
"@xylabs/tsconfig": "~7.2.
|
|
69
|
+
"@xylabs/ts-scripts-yarn3": "~7.2.32",
|
|
70
|
+
"@xylabs/tsconfig": "~7.2.32",
|
|
71
71
|
"@xyo-network/account": "~5.2.17",
|
|
72
72
|
"@xyo-network/account-model": "~5.2.17",
|
|
73
73
|
"@xyo-network/archivist-abstract": "~5.2.17",
|
|
74
74
|
"@xyo-network/bios-model": "~7.2.0",
|
|
75
75
|
"@xyo-network/boundwitness-builder": "~5.2.17",
|
|
76
|
-
"@xyo-network/chain-services": "~1.
|
|
76
|
+
"@xyo-network/chain-services": "~1.18.0",
|
|
77
77
|
"@xyo-network/module-abstract-mongodb": "~5.2.17",
|
|
78
78
|
"@xyo-network/module-model-mongodb": "~5.2.17",
|
|
79
79
|
"@xyo-network/node-memory": "~5.2.17",
|
|
80
80
|
"@xyo-network/xl1-protocol": "~1.14.17",
|
|
81
81
|
"dotenv": "~17.2.3",
|
|
82
|
-
"eslint": "^9.39.
|
|
82
|
+
"eslint": "^9.39.2",
|
|
83
83
|
"nodemon": "~3.1.11",
|
|
84
84
|
"tslib": "~2.8.1",
|
|
85
85
|
"typescript": "~5.9.3",
|
package/src/ProducerActor.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { Attributes, Counter } from '@opentelemetry/api'
|
|
2
2
|
import type { CreatableName } from '@xylabs/sdk-js'
|
|
3
3
|
import {
|
|
4
|
-
asAddress,
|
|
5
4
|
creatable, isDefined, isUndefined, toHex,
|
|
6
|
-
ZERO_ADDRESS,
|
|
7
5
|
} from '@xylabs/sdk-js'
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { SimpleBlockRunner
|
|
6
|
+
import {
|
|
7
|
+
Actor, type ActorParams, initProducerAccount,
|
|
8
|
+
} from '@xyo-network/chain-orchestration'
|
|
9
|
+
import { SimpleBlockRunner } from '@xyo-network/chain-services'
|
|
12
10
|
import type {
|
|
13
11
|
ChainId,
|
|
14
12
|
ChainStakeIntent, HydratedBlockWithHashMeta,
|
|
@@ -16,87 +14,82 @@ import type {
|
|
|
16
14
|
} from '@xyo-network/xl1-protocol'
|
|
17
15
|
import { asXL1BlockNumber } from '@xyo-network/xl1-protocol'
|
|
18
16
|
import {
|
|
19
|
-
AccountBalanceViewer,
|
|
20
|
-
|
|
17
|
+
AccountBalanceViewer, AccountBalanceViewerMoniker, BlockRunner, BlockRunnerMoniker,
|
|
18
|
+
BlockViewer, BlockViewerMoniker, buildTransaction,
|
|
21
19
|
Config,
|
|
20
|
+
createDeclarationIntent,
|
|
21
|
+
getDefaultConfig,
|
|
22
22
|
MempoolRunner,
|
|
23
|
+
MempoolRunnerMoniker,
|
|
23
24
|
MempoolViewer,
|
|
25
|
+
MempoolViewerMoniker,
|
|
24
26
|
StakeTotalsViewer,
|
|
25
|
-
|
|
27
|
+
StakeTotalsViewerMoniker,
|
|
26
28
|
} from '@xyo-network/xl1-protocol-sdk'
|
|
27
29
|
import { Mutex } from 'async-mutex'
|
|
28
30
|
|
|
29
|
-
export type ProducerActorParams = ActorParams
|
|
30
|
-
account: AccountInstance
|
|
31
|
-
blockRewardViewer: BlockRewardViewer
|
|
32
|
-
chainId: ChainId
|
|
33
|
-
config: Config
|
|
34
|
-
runners: {
|
|
35
|
-
mempoolRunner: MempoolRunner
|
|
36
|
-
}
|
|
37
|
-
viewers: {
|
|
38
|
-
accountBalanceViewer: AccountBalanceViewer
|
|
39
|
-
blockViewer: BlockViewer
|
|
40
|
-
mempoolViewer: MempoolViewer
|
|
41
|
-
stakeTotalsViewer: StakeTotalsViewer
|
|
42
|
-
time: TimeSyncViewer
|
|
43
|
-
}
|
|
44
|
-
}>
|
|
31
|
+
export type ProducerActorParams = ActorParams
|
|
45
32
|
|
|
46
33
|
const SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true
|
|
47
34
|
const TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds
|
|
48
35
|
|
|
49
36
|
@creatable()
|
|
50
37
|
export class ProducerActor extends Actor<ProducerActorParams> {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
protected
|
|
54
|
-
protected
|
|
55
|
-
protected
|
|
56
|
-
protected
|
|
57
|
-
protected
|
|
38
|
+
static readonly RedeclarationWindow = 10_000
|
|
39
|
+
|
|
40
|
+
protected _lastProducedBlock?: HydratedBlockWithHashMeta
|
|
41
|
+
protected _lastRedeclarationIntent?: ChainStakeIntent
|
|
42
|
+
protected _metricAttributes?: Attributes
|
|
43
|
+
protected _producerActorBlockProductionAttempts?: Counter<Attributes>
|
|
44
|
+
protected _producerActorBlockProductionChecks?: Counter<Attributes>
|
|
45
|
+
protected _producerActorBlocksProduced?: Counter<Attributes>
|
|
46
|
+
protected _producerActorBlocksPublished?: Counter<Attributes>
|
|
47
|
+
|
|
48
|
+
private _accountBalanceViewer?: AccountBalanceViewer
|
|
49
|
+
private _blockRunner?: BlockRunner
|
|
50
|
+
private _blockViewer?: BlockViewer
|
|
51
|
+
private _chainId?: ChainId
|
|
52
|
+
private _mempoolRunner?: MempoolRunner
|
|
53
|
+
private _mempoolViewer?: MempoolViewer
|
|
58
54
|
|
|
59
55
|
private _produceBlockMutex = new Mutex()
|
|
60
|
-
private _producer: BlockProducerService | undefined
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
return this.params.account!
|
|
64
|
-
}
|
|
57
|
+
private _stakeTotalsViewer?: StakeTotalsViewer
|
|
65
58
|
|
|
66
59
|
protected get accountBalanceViewer() {
|
|
67
|
-
return this.
|
|
60
|
+
return this._accountBalanceViewer!
|
|
68
61
|
}
|
|
69
62
|
|
|
70
|
-
protected get
|
|
71
|
-
return this.
|
|
63
|
+
protected get blockRunner() {
|
|
64
|
+
return this._blockRunner!
|
|
72
65
|
}
|
|
73
66
|
|
|
74
|
-
protected get
|
|
75
|
-
return this.
|
|
67
|
+
protected get blockViewer() {
|
|
68
|
+
return this._blockViewer!
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
protected get
|
|
79
|
-
return this.
|
|
71
|
+
protected get chainId() {
|
|
72
|
+
return this._chainId!
|
|
80
73
|
}
|
|
81
74
|
|
|
82
75
|
protected get mempoolRunner() {
|
|
83
|
-
return this.
|
|
76
|
+
return this._mempoolRunner!
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
protected get mempoolViewer() {
|
|
87
|
-
return this.
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
protected get producer() {
|
|
91
|
-
return this._producer!
|
|
80
|
+
return this._mempoolViewer!
|
|
92
81
|
}
|
|
93
82
|
|
|
94
83
|
protected get stakeTotalsViewer() {
|
|
95
|
-
return this.
|
|
84
|
+
return this._stakeTotalsViewer!
|
|
96
85
|
}
|
|
97
86
|
|
|
98
87
|
static override async paramsHandler<T extends ActorParams>(params?: Partial<T>) {
|
|
99
|
-
|
|
88
|
+
const config: Config = params?.context?.config ?? getDefaultConfig()
|
|
89
|
+
const account = params?.account ?? await initProducerAccount({ config })
|
|
90
|
+
return await super.paramsHandler({
|
|
91
|
+
...params, name: (params?.name ?? 'Producer') as CreatableName, account,
|
|
92
|
+
})
|
|
100
93
|
}
|
|
101
94
|
|
|
102
95
|
override async createHandler() {
|
|
@@ -121,21 +114,12 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
121
114
|
'producer_actor_blocks_published',
|
|
122
115
|
{ description: 'Number of blocks published' },
|
|
123
116
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
mempoolViewer: this.mempoolViewer,
|
|
131
|
-
blockRewardViewer: this.params.blockRewardViewer!,
|
|
132
|
-
rewardAddress: asAddress(
|
|
133
|
-
this.config.producer.rewardAddress ?? ZERO_ADDRESS,
|
|
134
|
-
() => `Producer config must have a valid reward address configured [${this.params.config.producer.rewardAddress}]`,
|
|
135
|
-
),
|
|
136
|
-
time: this.params.viewers.time!,
|
|
137
|
-
} satisfies SimpleBlockRunnerParams
|
|
138
|
-
this._producer = await SimpleBlockRunner.create(params)
|
|
117
|
+
this._accountBalanceViewer = await this.locator.getInstance<AccountBalanceViewer>(AccountBalanceViewerMoniker)
|
|
118
|
+
this._blockRunner = await this.locator.getInstance<BlockRunner>(BlockRunnerMoniker)
|
|
119
|
+
this._blockViewer = await this.locator.getInstance<BlockViewer>(BlockViewerMoniker)
|
|
120
|
+
this._mempoolRunner = await this.locator.getInstance<MempoolRunner>(MempoolRunnerMoniker)
|
|
121
|
+
this._mempoolViewer = await this.locator.getInstance<MempoolViewer>(MempoolViewerMoniker)
|
|
122
|
+
this._stakeTotalsViewer = await this.locator.getInstance<StakeTotalsViewer>(StakeTotalsViewerMoniker)
|
|
139
123
|
}
|
|
140
124
|
|
|
141
125
|
override async startHandler() {
|
|
@@ -153,23 +137,9 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
153
137
|
}
|
|
154
138
|
}
|
|
155
139
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// // TODO: This doesn't handle the case where the producer had declared a range for the future
|
|
160
|
-
// // but we're in a range that's not the future
|
|
161
|
-
// // Sort in ascending order based on ending range to get range with highest ending block
|
|
162
|
-
// const lastRange = ranges.toSorted((a, b) => a[1] > b[1] ? 1 : -1).at(-1)
|
|
163
|
-
|
|
164
|
-
// // Use the most recent range's end block as the current declaration end OR
|
|
165
|
-
// const [, currentDeclarationEnd] = lastRange
|
|
166
|
-
// // If we have no ranges, we need to declare intent, so use the current block
|
|
167
|
-
// ?? [undefined, currentBlock]
|
|
168
|
-
|
|
169
|
-
// // Calculate the time until the producer's declaration expires
|
|
170
|
-
// const timeToProducerExpiration = currentDeclarationEnd - currentBlock
|
|
171
|
-
// return timeToProducerExpiration
|
|
172
|
-
// }
|
|
140
|
+
protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number {
|
|
141
|
+
return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock
|
|
142
|
+
}
|
|
173
143
|
|
|
174
144
|
protected async produceBlock(): Promise<void> {
|
|
175
145
|
this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)
|
|
@@ -181,10 +151,7 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
181
151
|
|
|
182
152
|
await this._produceBlockMutex.runExclusive(async () => {
|
|
183
153
|
// Get the updated head
|
|
184
|
-
const headStart = Date.now()
|
|
185
154
|
const head = (await this.blockViewer.currentBlock())[0]
|
|
186
|
-
const headDuration = Date.now() - headStart
|
|
187
|
-
if (headDuration > 500) this.logger?.warn(`[Slow] Fetched head in ${headDuration}ms: 0x${toHex(head._hash)}`)
|
|
188
155
|
// Check if we've already produced the next block for this head
|
|
189
156
|
const headHash = head._hash
|
|
190
157
|
// If our last produced block was the next block for the current head, we do not
|
|
@@ -194,10 +161,7 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
194
161
|
} else {
|
|
195
162
|
this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)
|
|
196
163
|
// Produce the next block
|
|
197
|
-
const
|
|
198
|
-
const nextBlock = await this.producer.next(head)
|
|
199
|
-
const nextDuration = Date.now() - nextStart
|
|
200
|
-
if (nextDuration > 1000) this.logger?.warn(`[Slow] Generated next block in ${nextDuration}ms, block: ${nextBlock?.[0]._hash}`)
|
|
164
|
+
const nextBlock = await this.blockRunner.next(head)
|
|
201
165
|
// If it was produced
|
|
202
166
|
if (nextBlock) {
|
|
203
167
|
const displayBlockNumber = `0x${toHex(nextBlock[0].block)}`
|
|
@@ -220,7 +184,7 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
220
184
|
protected async redeclareIntent(): Promise<void> {
|
|
221
185
|
await this.spanAsync('redeclareIntent', async () => {
|
|
222
186
|
// Decide if we should redeclare intent
|
|
223
|
-
if (this.
|
|
187
|
+
if (this.config.producer.disableIntentRedeclaration) return
|
|
224
188
|
|
|
225
189
|
// Get the current block
|
|
226
190
|
const head = (await this.blockViewer.currentBlock())[0]
|
|
@@ -228,16 +192,16 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
228
192
|
const currentBlock = head.block
|
|
229
193
|
|
|
230
194
|
// // Calculate the time until the producer's declaration expires
|
|
231
|
-
|
|
195
|
+
const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)
|
|
232
196
|
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
197
|
+
// Allow the producer time to redeclare itself via block production
|
|
198
|
+
// (for free) before submitting a redeclaration intent transaction.
|
|
199
|
+
if (blocksUntilExpiration > ProducerActor.RedeclarationWindow * 0.1) {
|
|
200
|
+
// Clear any previous redeclaration intent
|
|
201
|
+
this._lastRedeclarationIntent = undefined
|
|
202
|
+
// No need to redeclare yet
|
|
203
|
+
return
|
|
204
|
+
}
|
|
241
205
|
|
|
242
206
|
// If we already have a valid redeclaration intent, do not create another
|
|
243
207
|
// unless it has expired.
|
|
@@ -259,7 +223,7 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
259
223
|
// Check if we have a valid stake before declaring intent
|
|
260
224
|
if (!(await this.validateCurrentStake())) {
|
|
261
225
|
this.logger?.error(
|
|
262
|
-
`Add stake to contract address ${this.
|
|
226
|
+
`Add stake to contract address ${this.config.chain.id}`
|
|
263
227
|
+ ' for the producer to declare it\'s intent.',
|
|
264
228
|
)
|
|
265
229
|
return
|
|
@@ -304,7 +268,7 @@ export class ProducerActor extends Actor<ProducerActorParams> {
|
|
|
304
268
|
// Check if we have a valid balance before declaring intent
|
|
305
269
|
const head = this._lastProducedBlock?.[0]._hash
|
|
306
270
|
if (isDefined(head)) {
|
|
307
|
-
const balances = await this.accountBalanceViewer.accountBalances([this.account.address], head)
|
|
271
|
+
const balances = await this.accountBalanceViewer.accountBalances([this.account.address], { head })
|
|
308
272
|
const currentBalance = balances[this.account.address] ?? 0n
|
|
309
273
|
if (currentBalance <= 0n) {
|
|
310
274
|
this.logger?.error(`Producer ${this.account.address} has no balance.`)
|
package/src/runProducer.ts
CHANGED
|
@@ -1,91 +1,64 @@
|
|
|
1
1
|
import type { Address, Logger } from '@xylabs/sdk-js'
|
|
2
|
-
import { assertEx, exists } from '@xylabs/sdk-js'
|
|
3
|
-
import { initProducerAccount, type OrchestratorInstance } from '@xyo-network/chain-orchestration'
|
|
4
2
|
import {
|
|
5
|
-
|
|
6
|
-
} from '@
|
|
3
|
+
asAddress, assertEx, exists,
|
|
4
|
+
} from '@xylabs/sdk-js'
|
|
5
|
+
import type { AccountInstance } from '@xyo-network/account-model'
|
|
7
6
|
import {
|
|
8
|
-
type
|
|
7
|
+
type ActorContext, initProducerAccount, type OrchestratorInstance,
|
|
8
|
+
} from '@xyo-network/chain-orchestration'
|
|
9
|
+
import { validateHydratedBlock, validateHydratedBlockState } from '@xyo-network/chain-validation'
|
|
10
|
+
import { SimpleBlockRunner } from '@xyo-network/chain-services'
|
|
11
|
+
import type {
|
|
12
|
+
Config, CreatableProviderContext, ProviderFactoryLocatorInstance,
|
|
9
13
|
} from '@xyo-network/xl1-protocol-sdk'
|
|
14
|
+
import { SimpleBlockRewardViewer, SimpleBlockValidationViewer } from '@xyo-network/xl1-protocol-sdk'
|
|
15
|
+
import { buildJsonRpcProviderLocator } from '@xyo-network/xl1-providers'
|
|
10
16
|
import {
|
|
11
|
-
|
|
12
|
-
JsonRpcBlockViewer,
|
|
13
|
-
JsonRpcMempoolRunner,
|
|
14
|
-
JsonRpcMempoolViewer,
|
|
15
|
-
JsonRpcStakeTotalsViewer,
|
|
16
|
-
MempoolRunnerRpcSchemas,
|
|
17
|
-
MempoolViewerRpcSchemas,
|
|
18
|
-
StakeTotalsViewerRpcSchemas,
|
|
17
|
+
HttpRpcTransport, type RpcSchemaMap, type TransportFactory,
|
|
19
18
|
} from '@xyo-network/xl1-rpc'
|
|
20
19
|
|
|
21
20
|
import { ProducerActor, type ProducerActorParams } from './ProducerActor.ts'
|
|
22
21
|
|
|
23
22
|
interface RunProducerContext {
|
|
24
23
|
config: Config
|
|
24
|
+
locator?: ProviderFactoryLocatorInstance
|
|
25
25
|
logger: Logger
|
|
26
26
|
orchestrator: OrchestratorInstance
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const getProviderFactoryLocator = async (
|
|
30
|
+
context: Omit<CreatableProviderContext, 'locator'>,
|
|
31
|
+
account: AccountInstance,
|
|
32
|
+
rewardAddress: Address,
|
|
33
|
+
) => {
|
|
34
|
+
const endpoint = assertEx(context.config.services.apiEndpoint, () => 'API endpoint is required in config.services.apiEndpoint')
|
|
35
|
+
const transportFactory: TransportFactory = (schemas: RpcSchemaMap) => new HttpRpcTransport(endpoint, schemas)
|
|
36
|
+
const locator = await buildJsonRpcProviderLocator({ context, transportFactory })
|
|
37
|
+
locator.register(SimpleBlockRewardViewer.factory())
|
|
38
|
+
locator.register(SimpleBlockValidationViewer.factory<SimpleBlockValidationViewer>({ state: validateHydratedBlockState, value: validateHydratedBlock }))
|
|
39
|
+
locator.register(SimpleBlockRunner.factory<SimpleBlockRunner>({ account, rewardAddress }))
|
|
40
|
+
return locator
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const runProducer = async ({
|
|
44
|
+
config, logger, orchestrator,
|
|
45
|
+
}: RunProducerContext) => {
|
|
46
|
+
const account = await initProducerAccount({ config })
|
|
47
|
+
logger.info(`Running producer for account ${account.address}`)
|
|
48
|
+
const rewardAddress = asAddress(config.producer.rewardAddress, () => 'rewardAddress is required in config.producer')
|
|
49
|
+
logger.info(`Using reward address ${rewardAddress}`)
|
|
33
50
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const mempoolRunner = new JsonRpcMempoolRunner(new HttpRpcTransport(endpoint, { ...MempoolRunnerRpcSchemas }))
|
|
42
|
-
const chainService = await initChainService({
|
|
43
|
-
account, config, logger,
|
|
44
|
-
})
|
|
45
|
-
const chainContractViewer = {
|
|
46
|
-
forkedAtBlockNumber: function (): Promise<bigint> {
|
|
47
|
-
return chainService.forkedAtBlockNumber()
|
|
48
|
-
},
|
|
49
|
-
forkedAtHash: function (): Promise<bigint> {
|
|
50
|
-
return chainService.forkedAtHash()
|
|
51
|
-
},
|
|
52
|
-
forkedChainId: function (): Promise<Address> {
|
|
53
|
-
return chainService.forkedChainId()
|
|
54
|
-
},
|
|
55
|
-
minWithdrawalBlocks: function (): Promise<bigint> {
|
|
56
|
-
return chainService.minWithdrawalBlocks()
|
|
57
|
-
},
|
|
58
|
-
rewardsContract: function (): Promise<string> {
|
|
59
|
-
return chainService.rewardsContract()
|
|
60
|
-
},
|
|
61
|
-
stakingTokenAddress: function (): Promise<string> {
|
|
62
|
-
return chainService.stakingTokenAddress()
|
|
63
|
-
},
|
|
64
|
-
moniker: ChainContractViewerMoniker,
|
|
65
|
-
} satisfies ChainContractViewer
|
|
66
|
-
const blockRewardViewer = await initBlockRewardViewer({
|
|
67
|
-
config, logger, chainContractViewer,
|
|
68
|
-
})
|
|
69
|
-
const timeSyncViewer = await initTimeService({
|
|
70
|
-
config, logger, blockViewer,
|
|
71
|
-
})
|
|
51
|
+
const locator = await getProviderFactoryLocator({
|
|
52
|
+
config, logger, singletons: {},
|
|
53
|
+
}, account, rewardAddress)
|
|
54
|
+
|
|
55
|
+
const context: ActorContext = {
|
|
56
|
+
locator, config, singletons: locator.context.singletons,
|
|
57
|
+
}
|
|
72
58
|
|
|
73
59
|
// Create actors
|
|
74
60
|
const params = {
|
|
75
|
-
account,
|
|
76
|
-
id: 'ProducerActor',
|
|
77
|
-
chainId,
|
|
78
|
-
config,
|
|
79
|
-
viewers: {
|
|
80
|
-
accountBalanceViewer,
|
|
81
|
-
blockViewer,
|
|
82
|
-
stakeTotalsViewer,
|
|
83
|
-
mempoolViewer,
|
|
84
|
-
time: timeSyncViewer,
|
|
85
|
-
},
|
|
86
|
-
blockRewardViewer,
|
|
87
|
-
runners: { mempoolRunner },
|
|
88
|
-
logger,
|
|
61
|
+
id: 'ProducerActor', context, account,
|
|
89
62
|
} satisfies ProducerActorParams
|
|
90
63
|
// const apiEndpoint = config.services.apiEndpoint
|
|
91
64
|
// const balances = isDefined(apiEndpoint) ? undefined : await BalanceActor.create(params)
|