From 4a0a6e884a8142eb1821e5ebd203502242e4ffa0 Mon Sep 17 00:00:00 2001 From: hui-an-yang <106410553+hui-an-yang@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:39:35 -0800 Subject: [PATCH] 2599 oxford2 supports attestation (#2775) * feat: supports attesation along side endorsement re #2599 * feat: the rest of files of supports attesation along side endorsement * test: fix protocol logic in integration-tests/rpc-nodes.spec.ts * test: addressed pr comments of naming * test: updated contract-estimation-tests.spec.ts assertions --- cspell.json | 3 + docs/consensus_key.md | 2 +- .../example-streamer-custom-retry-logic.ts | 6 +- example/example-streamer.ts | 6 +- integration-tests/data/allTestsCases.ts | 81 ++++---- integration-tests/local-forging.spec.ts | 9 +- integration-tests/rpc-nodes.spec.ts | 17 +- .../src/rpc-wrapper.ts | 8 + .../taquito-local-forging/src/constants.ts | 3 +- packages/taquito-local-forging/src/decoder.ts | 3 + packages/taquito-local-forging/src/encoder.ts | 2 + .../src/schema/operation.ts | 7 + .../taquito-local-forging/src/validator.ts | 3 + packages/taquito-rpc/src/opkind.ts | 5 + .../taquito-rpc/src/rpc-client-interface.ts | 7 + .../src/rpc-client-modules/rpc-cache.ts | 30 +++ packages/taquito-rpc/src/taquito-rpc.ts | 23 +++ packages/taquito-rpc/src/types.ts | 174 +++++++++++++++++- .../taquito-rpc/test/data/rpc-responses.ts | 13 ++ packages/taquito-rpc/test/rpc-cache.spec.ts | 13 ++ .../taquito-utils/src/verify-signature.ts | 2 +- packages/taquito/README.md | 20 +- packages/taquito/src/signer/interface.ts | 2 +- packages/taquito/src/subscribe/filters.ts | 2 + 24 files changed, 371 insertions(+), 70 deletions(-) diff --git a/cspell.json b/cspell.json index 5d5b5bbd6e..7babb37399 100644 --- a/cspell.json +++ b/cspell.json @@ -69,7 +69,10 @@ "oxfordnet", "oxheadalpha", "Pkhfrom", + "preattestation", "precommit", + "preendorsement", + "preendorsements", "prevalidated", "prevalidation", "println", diff --git a/docs/consensus_key.md b/docs/consensus_key.md index 7b9b84b478..bda600b95d 100644 --- a/docs/consensus_key.md +++ b/docs/consensus_key.md @@ -3,7 +3,7 @@ title: Consensus Keys author: Davis Sawali & Hui-An Yang --- -The "consensus key" feature allows bakers to use a different key, called the consensus key. It will allow for baking and signing consensus operations (i.e. pre-endorsements and endorsements). For more detailed information on consensus keys, refer to [this documentation](https://tezos.gitlab.io/protocols/015_lima.html?highlight=update%20consensus%20key#consensus-key) +The "consensus key" feature allows bakers to use a different key, called the consensus key. It will allow for baking and signing consensus operations (i.e. preattestation/preendorsements and attestation/endorsements). For more detailed information on consensus keys, refer to [this documentation](https://tezos.gitlab.io/protocols/015_lima.html?highlight=update%20consensus%20key#consensus-key) Starting from Lima protocol, these 2 new operations will be available: diff --git a/example/example-streamer-custom-retry-logic.ts b/example/example-streamer-custom-retry-logic.ts index 7009bcd7a5..4a5194bb0b 100644 --- a/example/example-streamer-custom-retry-logic.ts +++ b/example/example-streamer-custom-retry-logic.ts @@ -20,6 +20,10 @@ async function example() { ) as any })); + const bakerAttestationFilter = { + and: [{ source: 'tz2TSvNTh2epDMhZHrw73nV9piBX7kLZ9K9m' }, { kind: 'attestation' }] + } + const bakerEndorsementFilter = { and: [{ source: 'tz2TSvNTh2epDMhZHrw73nV9piBX7kLZ9K9m' }, { kind: 'endorsement' }] } @@ -29,7 +33,7 @@ async function example() { } tezos.stream.subscribeOperation({ - or: [bakerEndorsementFilter, bakerDelegation] + or: [bakerAttestationFilter, bakerEndorsementFilter, bakerDelegation] }) } diff --git a/example/example-streamer.ts b/example/example-streamer.ts index 9653adad81..91d61bee55 100644 --- a/example/example-streamer.ts +++ b/example/example-streamer.ts @@ -6,6 +6,10 @@ async function example() { tezos.setStreamProvider(tezos.getFactory(PollingSubscribeProvider)({ shouldObservableSubscriptionRetry: true, pollingIntervalMilliseconds: 15000 })); try { + const bakerAttestationFilter = { + and: [{ source: 'tz1bQMn5xYFbX6geRxqvuAiTywsCtNywawxH' }, { kind: 'attestation' }] + } + const bakerEndorsementFilter = { and: [{ source: 'tz1bQMn5xYFbX6geRxqvuAiTywsCtNywawxH' }, { kind: 'endorsement' }] } @@ -15,7 +19,7 @@ async function example() { } const sub = tezos.stream.subscribeOperation({ - or: [bakerEndorsementFilter, bakerDelegation] + or: [bakerAttestationFilter, bakerEndorsementFilter, bakerDelegation] }) sub.on('data', console.log) diff --git a/integration-tests/data/allTestsCases.ts b/integration-tests/data/allTestsCases.ts index 5794242a08..70c63b2a3f 100644 --- a/integration-tests/data/allTestsCases.ts +++ b/integration-tests/data/allTestsCases.ts @@ -48,36 +48,18 @@ interface TestCase { expected?: object; } -export const nairobiCases: TestCase[] = [ +export const oxfordCases: TestCase[] = [ { - name: 'Set deposits limit 1000000', + name: 'Attestation', operation: { branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX', contents: [ { - kind: OpKind.SET_DEPOSITS_LIMIT, - counter: '1', - source: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn', - fee: '10000', - gas_limit: '10', - storage_limit: '10', - limit: '1000000', - }, - ], - }, - }, - { - name: 'Unset deposits limit', - operation: { - branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX', - contents: [ - { - kind: OpKind.SET_DEPOSITS_LIMIT, - counter: '1', - source: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn', - fee: '10000', - gas_limit: '10', - storage_limit: '10' + kind: OpKind.ATTESTATION, + slot: 0, + level: 66299, + round: 5, + block_payload_hash: 'vh3FEkypvxUYLwjGYd2Sme7aWyfX8npDsqxcL6imVpBWnAZeNn2n', }, ], }, @@ -134,6 +116,40 @@ export const commonCases: TestCase[] = [ ], }, }, + + { + name: 'Set deposits limit 1000000', + operation: { + branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX', + contents: [ + { + kind: OpKind.SET_DEPOSITS_LIMIT, + counter: '1', + source: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn', + fee: '10000', + gas_limit: '10', + storage_limit: '10', + limit: '1000000', + }, + ], + }, + }, + { + name: 'Unset deposits limit', + operation: { + branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX', + contents: [ + { + kind: OpKind.SET_DEPOSITS_LIMIT, + counter: '1', + source: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn', + fee: '10000', + gas_limit: '10', + storage_limit: '10' + }, + ], + }, + }, { name: 'Seed nonce revelation', operation: { @@ -1171,21 +1187,6 @@ export const commonCases: TestCase[] = [ ], }, }, - { - name: 'Endorsement', - operation: { - branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX', - contents: [ - { - kind: OpKind.ENDORSEMENT, - slot: 0, - level: 66299, - round: 5, - block_payload_hash: 'vh3FEkypvxUYLwjGYd2Sme7aWyfX8npDsqxcL6imVpBWnAZeNn2n', - }, - ], - }, - }, { name: `Origination of a contract that contains the instructions SUB_MUTEZ`, operation: { diff --git a/integration-tests/local-forging.spec.ts b/integration-tests/local-forging.spec.ts index 25a9435c4e..1e4028a14a 100644 --- a/integration-tests/local-forging.spec.ts +++ b/integration-tests/local-forging.spec.ts @@ -1,16 +1,17 @@ import { CONFIGS } from "./config"; -import { commonCases, nairobiCases } from './data/allTestsCases'; +import { commonCases, oxfordCases } from './data/allTestsCases'; import { LocalForger, ProtocolsHash } from '@taquito/local-forging' import { Protocols, TezosToolkit } from "@taquito/taquito"; +import { ProtoGreaterOrEqual } from '@taquito/michel-codec' CONFIGS().forEach(({ rpc, protocol }) => { const Tezos = new TezosToolkit(rpc); - const nairobinet = protocol === Protocols.PtNairobi ? it : it.skip; + const oxfordAndAlpha = ProtoGreaterOrEqual(protocol, Protocols.ProxfordY) ? test : test.skip describe(`Test local forger: ${rpc}`, () => { // all protocols - nairobiCases.forEach(({ name, operation, expected }) => { - nairobinet(`Verify that .forge for local forge will return same result as for network forge for rpc: ${name} (${rpc})`, async () => { + oxfordCases.forEach(({ name, operation, expected }) => { + oxfordAndAlpha(`Verify that .forge for local forge will return same result as for network forge for rpc: ${name} (${rpc})`, async () => { const localForger = new LocalForger(protocol as unknown as ProtocolsHash); const result = await localForger.forge(operation); const rpcResult = await Tezos.rpc.forgeOperations(operation); diff --git a/integration-tests/rpc-nodes.spec.ts b/integration-tests/rpc-nodes.spec.ts index 667cf94f9c..304037a84f 100644 --- a/integration-tests/rpc-nodes.spec.ts +++ b/integration-tests/rpc-nodes.spec.ts @@ -21,7 +21,7 @@ CONFIGS().forEach( }) => { const Tezos = lib; const unrestrictedRPCNode = rpc.endsWith("ecadinfra.com") ? test.skip : test; - const oxfordAndAlpha = ProtoGreaterOrEqual(protocol, Protocols.ProxfordY) ? test : test.skip + const oxfordAndAlpha = ProtoGreaterOrEqual(protocol, Protocols.ProxfordY) ? test : test.skip; let ticketContract: DefaultContractType; @@ -166,6 +166,19 @@ CONFIGS().forEach( expect(bakingRights[0].priority).toBeUndefined(); }); + unrestrictedRPCNode('Verify that rpcClient.getAttestationRights retrieves the list of delegates allowed to attest a block', async () => { + const attestationRights = await rpcClient.getAttestationRights(); + expect(attestationRights).toBeDefined(); + expect(attestationRights[0].delegates).toBeDefined(); + expect(attestationRights[0].delegates![0].delegate).toBeDefined(); + expect(typeof attestationRights[0].delegates![0].delegate).toEqual('string'); + expect(attestationRights[0].delegates![0].attestation_power).toBeDefined(); + expect(typeof attestationRights[0].delegates![0].attestation_power).toEqual('number'); + expect(attestationRights[0].delegates![0].first_slot).toBeDefined(); + expect(typeof attestationRights[0].delegates![0].first_slot).toEqual('number'); + expect(attestationRights[0].delegate).toBeUndefined(); + }); + unrestrictedRPCNode('Verify that rpcClient.getEndorsingRights retrieves the list of delegates allowed to endorse a block', async () => { const endorsingRights = await rpcClient.getEndorsingRights(); expect(endorsingRights).toBeDefined(); @@ -448,7 +461,7 @@ CONFIGS().forEach( }); it('Verify that rpcClient.getPendingOperations v1 will retrieve the pending operations in mempool with property applied', async () => { - const pendingOperations = await rpcClient.getPendingOperations({ version: '1'}) as PendingOperationsV1; + const pendingOperations = await rpcClient.getPendingOperations({ version: '1' }) as PendingOperationsV1; expect(pendingOperations).toBeDefined(); expect(pendingOperations.applied).toBeInstanceOf(Array); expect(pendingOperations.refused).toBeInstanceOf(Array); diff --git a/packages/taquito-contracts-library/src/rpc-wrapper.ts b/packages/taquito-contracts-library/src/rpc-wrapper.ts index f47f41bd42..d7d50d84e4 100644 --- a/packages/taquito-contracts-library/src/rpc-wrapper.ts +++ b/packages/taquito-contracts-library/src/rpc-wrapper.ts @@ -17,6 +17,8 @@ import { DelegateResponse, DelegatesResponse, VotingInfoResponse, + AttestationRightsQueryArguments, + AttestationRightsResponse, EndorsingRightsQueryArguments, EndorsingRightsResponse, EntrypointsResponse, @@ -172,6 +174,12 @@ export class RpcWrapperContractsLibrary implements RpcClientInterface { ): Promise { return this.rpc.getBakingRights(args, { block }); } + async getAttestationRights( + args: AttestationRightsQueryArguments, + { block }: RPCOptions = defaultRPCOptions + ): Promise { + return this.rpc.getAttestationRights(args, { block }); + } async getEndorsingRights( args: EndorsingRightsQueryArguments, { block }: RPCOptions = defaultRPCOptions diff --git a/packages/taquito-local-forging/src/constants.ts b/packages/taquito-local-forging/src/constants.ts index 8def4779ee..dd5dccb645 100644 --- a/packages/taquito-local-forging/src/constants.ts +++ b/packages/taquito-local-forging/src/constants.ts @@ -42,6 +42,7 @@ export enum CODEC { OP_ORIGINATION = 'origination', OP_BALLOT = 'ballot', OP_FAILING_NOOP = 'failing_noop', + OP_ATTESTATION = 'attestation', OP_ENDORSEMENT = 'endorsement', OP_SEED_NONCE_REVELATION = 'seed_nonce_revelation', OP_REVEAL = 'reveal', @@ -237,7 +238,7 @@ export const kindMapping: { [key: number]: string } = { 0x6c: 'transaction', 0x6d: 'origination', 0x06: 'ballot', - 0x15: 'endorsement', + 0x15: 'attestation', 0x01: 'seed_nonce_revelation', 0x05: 'proposals', 0x6f: 'register_global_constant', diff --git a/packages/taquito-local-forging/src/decoder.ts b/packages/taquito-local-forging/src/decoder.ts index 68b1c13788..0bcfb7c212 100644 --- a/packages/taquito-local-forging/src/decoder.ts +++ b/packages/taquito-local-forging/src/decoder.ts @@ -31,6 +31,7 @@ import { ActivationSchema, BallotSchema, DelegationSchema, + AttestationSchema, EndorsementSchema, IncreasePaidStorageSchema, UpdateConsensusKeySchema, @@ -99,6 +100,8 @@ decoders[CODEC.OP_TRANSACTION] = (val: Uint8ArrayConsumer) => decoders[CODEC.OP_ORIGINATION] = (val: Uint8ArrayConsumer) => schemaDecoder(decoders)(OriginationSchema)(val); decoders[CODEC.OP_BALLOT] = (val: Uint8ArrayConsumer) => schemaDecoder(decoders)(BallotSchema)(val); +decoders[CODEC.OP_ATTESTATION] = (val: Uint8ArrayConsumer) => + schemaDecoder(decoders)(AttestationSchema)(val); decoders[CODEC.OP_ENDORSEMENT] = (val: Uint8ArrayConsumer) => schemaDecoder(decoders)(EndorsementSchema)(val); decoders[CODEC.OP_SEED_NONCE_REVELATION] = (val: Uint8ArrayConsumer) => diff --git a/packages/taquito-local-forging/src/encoder.ts b/packages/taquito-local-forging/src/encoder.ts index 028ffcb503..96f7f99133 100644 --- a/packages/taquito-local-forging/src/encoder.ts +++ b/packages/taquito-local-forging/src/encoder.ts @@ -31,6 +31,7 @@ import { ActivationSchema, BallotSchema, DelegationSchema, + AttestationSchema, EndorsementSchema, IncreasePaidStorageSchema, UpdateConsensusKeySchema, @@ -91,6 +92,7 @@ encoders[CODEC.OP_DELEGATION] = (val: any) => schemaEncoder(encoders)(Delegation encoders[CODEC.OP_TRANSACTION] = (val: any) => schemaEncoder(encoders)(TransactionSchema)(val); encoders[CODEC.OP_ORIGINATION] = (val: any) => schemaEncoder(encoders)(OriginationSchema)(val); encoders[CODEC.OP_BALLOT] = (val: any) => schemaEncoder(encoders)(BallotSchema)(val); +encoders[CODEC.OP_ATTESTATION] = (val: any) => schemaEncoder(encoders)(AttestationSchema)(val); encoders[CODEC.OP_ENDORSEMENT] = (val: any) => schemaEncoder(encoders)(EndorsementSchema)(val); encoders[CODEC.OP_SEED_NONCE_REVELATION] = (val: any) => schemaEncoder(encoders)(SeedNonceRevelationSchema)(val); diff --git a/packages/taquito-local-forging/src/schema/operation.ts b/packages/taquito-local-forging/src/schema/operation.ts index 73be533c74..3e9640065b 100644 --- a/packages/taquito-local-forging/src/schema/operation.ts +++ b/packages/taquito-local-forging/src/schema/operation.ts @@ -65,6 +65,13 @@ export const BallotSchema = { ballot: CODEC.BALLOT_STATEMENT, }; +export const AttestationSchema = { + slot: CODEC.INT16, + level: CODEC.INT32, + round: CODEC.INT32, + block_payload_hash: CODEC.BLOCK_PAYLOAD_HASH, +}; + export const EndorsementSchema = { slot: CODEC.INT16, level: CODEC.INT32, diff --git a/packages/taquito-local-forging/src/validator.ts b/packages/taquito-local-forging/src/validator.ts index 864891e158..59d277f634 100644 --- a/packages/taquito-local-forging/src/validator.ts +++ b/packages/taquito-local-forging/src/validator.ts @@ -10,6 +10,7 @@ import { ProposalsSchema, RevealSchema, RegisterGlobalConstantSchema, + AttestationSchema, EndorsementSchema, TransferTicketSchema, IncreasePaidStorageSchema, @@ -29,6 +30,7 @@ type OperationKind = | OpKind.TRANSACTION | OpKind.ORIGINATION | OpKind.BALLOT + | OpKind.ATTESTATION | OpKind.ENDORSEMENT | OpKind.SEED_NONCE_REVELATION | OpKind.PROPOSALS @@ -49,6 +51,7 @@ const OperationKindMapping = { transaction: TransactionSchema, origination: OriginationSchema, ballot: BallotSchema, + attestation: AttestationSchema, endorsement: EndorsementSchema, seed_nonce_revelation: SeedNonceRevelationSchema, proposals: ProposalsSchema, diff --git a/packages/taquito-rpc/src/opkind.ts b/packages/taquito-rpc/src/opkind.ts index ed371165ff..80b6a47c21 100644 --- a/packages/taquito-rpc/src/opkind.ts +++ b/packages/taquito-rpc/src/opkind.ts @@ -4,12 +4,17 @@ export enum OpKind { REVEAL = 'reveal', TRANSACTION = 'transaction', ACTIVATION = 'activate_account', + ATTESTATION = 'attestation', ENDORSEMENT = 'endorsement', + PREATTESTATION = 'preattestation', PREENDORSEMENT = 'preendorsement', SET_DEPOSITS_LIMIT = 'set_deposits_limit', + DOUBLE_PREATTESTATION_EVIDENCE = 'double_preattestation_evidence', DOUBLE_PREENDORSEMENT_EVIDENCE = 'double_preendorsement_evidence', + ATTESTATION_WITH_SLOT = 'attestation_with_slot', ENDORSEMENT_WITH_SLOT = 'endorsement_with_slot', SEED_NONCE_REVELATION = 'seed_nonce_revelation', + DOUBLE_ATTESTATION_EVIDENCE = 'double_attestation_evidence', DOUBLE_ENDORSEMENT_EVIDENCE = 'double_endorsement_evidence', DOUBLE_BAKING_EVIDENCE = 'double_baking_evidence', PROPOSALS = 'proposals', diff --git a/packages/taquito-rpc/src/rpc-client-interface.ts b/packages/taquito-rpc/src/rpc-client-interface.ts index eab2f52b73..815dda2650 100644 --- a/packages/taquito-rpc/src/rpc-client-interface.ts +++ b/packages/taquito-rpc/src/rpc-client-interface.ts @@ -17,6 +17,8 @@ import { CurrentQuorumResponse, DelegateResponse, DelegatesResponse, + AttestationRightsQueryArguments, + AttestationRightsResponse, EndorsingRightsQueryArguments, EndorsingRightsResponse, EntrypointsResponse, @@ -84,6 +86,10 @@ export interface RpcClientInterface { args: BakingRightsQueryArguments, options?: RPCOptions ): Promise; + getAttestationRights( + args: AttestationRightsQueryArguments, + options?: RPCOptions + ): Promise; getEndorsingRights( args: EndorsingRightsQueryArguments, options?: RPCOptions @@ -150,6 +156,7 @@ export enum RPCMethodName { GET_DELEGATE = 'getDelegate', GET_DELEGATES = 'getDelegates', GET_VOTING_INFO = 'getVotingInfo', + GET_ATTESTATION_RIGHTS = 'getAttestationRights', GET_ENDORSING_RIGHTS = 'getEndorsingRights', GET_ENTRYPOINTS = 'getEntrypoints', GET_LIVE_BLOCKS = 'getLiveBlocks', diff --git a/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts b/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts index badf6819b1..96498c3ab5 100644 --- a/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts +++ b/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts @@ -20,6 +20,8 @@ import { DelegateResponse, DelegatesResponse, VotingInfoResponse, + AttestationRightsQueryArguments, + AttestationRightsResponse, EndorsingRightsQueryArguments, EndorsingRightsResponse, EntrypointsResponse, @@ -72,6 +74,7 @@ type RpcMethodParam = | BigMapKey | BakingRightsQueryArguments | PendingOperationsQueryArguments + | AttestationRightsQueryArguments | EndorsingRightsQueryArguments; const defaultTtl = 1000; @@ -620,6 +623,33 @@ export class RpcClientCache implements RpcClientInterface { } } + /** + * + * @param args contains optional query arguments + * @param options contains generic configuration for rpc calls + * + * @description Retrieves the delegates allowed to attest a block. + * + * @see https://tezos.gitlab.io/api/rpc.html#get-block-id-helpers-attestation-rights + */ + async getAttestationRights( + args: AttestationRightsQueryArguments = {}, + { block }: RPCOptions = defaultRPCOptions + ): Promise { + const key = this.formatCacheKey( + this.rpcClient.getRpcUrl(), + RPCMethodName.GET_ATTESTATION_RIGHTS, + [block, args] + ); + if (this.has(key)) { + return this.get(key); + } else { + const response = this.rpcClient.getAttestationRights(args, { block }); + this.put(key, response); + return response; + } + } + /** * * @param args contains optional query arguments diff --git a/packages/taquito-rpc/src/taquito-rpc.ts b/packages/taquito-rpc/src/taquito-rpc.ts index 398328a839..d81541827d 100644 --- a/packages/taquito-rpc/src/taquito-rpc.ts +++ b/packages/taquito-rpc/src/taquito-rpc.ts @@ -34,6 +34,8 @@ import { DelegateResponse, DelegatesResponse, VotingInfoResponse, + AttestationRightsQueryArguments, + AttestationRightsResponse, EndorsingRightsQueryArguments, EndorsingRightsResponse, EntrypointsResponse, @@ -606,6 +608,27 @@ export class RpcClient implements RpcClientInterface { * @param args contains optional query arguments * @param options contains generic configuration for rpc calls to specified block (default as head) * + * @description Retrieves the delegates allowed to attest a block + * @see https://tezos.gitlab.io/api/rpc.html#get-block-id-helpers-endorsing-rights + */ + async getAttestationRights( + args: AttestationRightsQueryArguments = {}, + { block }: RPCOptions = defaultRPCOptions + ): Promise { + const response = await this.httpBackend.createRequest({ + url: this.createURL(`/chains/${this.chain}/blocks/${block}/helpers/attestation_rights`), + method: 'GET', + query: args, + }); + + return response; + } + + /** + * + * @param args contains optional query arguments + * @param options contains generic configuration for rpc calls + * * @description Retrieves the list of delegates allowed to bake a block. * * @see https://tezos.gitlab.io/api/rpc.html#get-block-id-helpers-endorsing-rights diff --git a/packages/taquito-rpc/src/types.ts b/packages/taquito-rpc/src/types.ts index 0764e875b3..341d961fdc 100644 --- a/packages/taquito-rpc/src/types.ts +++ b/packages/taquito-rpc/src/types.ts @@ -107,8 +107,18 @@ export interface BlockFullHeader { signature: string; } +export type InlinedAttestationKindEnum = OpKind.ATTESTATION; + export type InlinedEndorsementKindEnum = OpKind.ENDORSEMENT; +export interface InlinedAttestationContents { + kind: InlinedAttestationKindEnum; + slot?: number; + round?: number; + block_payload_hash?: string; + level: number; +} + export interface InlinedEndorsementContents { kind: InlinedEndorsementKindEnum; slot?: number; @@ -117,6 +127,14 @@ export interface InlinedEndorsementContents { level: number; } +export interface InlinedPreattestationContents { + kind: OpKind.PREATTESTATION; + slot: number; + level: number; + round: number; + block_payload_hash: string; +} + export interface InlinedPreEndorsementContents { kind: OpKind.PREENDORSEMENT; slot: number; @@ -125,12 +143,24 @@ export interface InlinedPreEndorsementContents { block_payload_hash: string; } +export interface InlinedAttestation { + branch: string; + operations: InlinedAttestationContents; + signature?: string; +} + export interface InlinedEndorsement { branch: string; operations: InlinedEndorsementContents; signature?: string; } +export interface InlinedPreattestation { + branch: string; + operations: InlinedPreattestationContents; + signature?: string; +} + export interface InlinedPreEndorsement { branch: string; operations: InlinedPreEndorsementContents; @@ -139,6 +169,14 @@ export interface InlinedPreEndorsement { export type BallotVote = 'nay' | 'yay' | 'pass'; +export interface OperationContentsAttestation { + kind: OpKind.ATTESTATION; + level: number; + slot?: number; + round?: number; + block_payload_hash?: string; +} + export interface OperationContentsEndorsement { kind: OpKind.ENDORSEMENT; level: number; @@ -147,6 +185,13 @@ export interface OperationContentsEndorsement { block_payload_hash?: string; } +export interface OperationContentsPreattestation { + kind: OpKind.PREATTESTATION; + slot: number; + level: number; + round: number; + block_payload_hash: string; +} export interface OperationContentsPreEndorsement { kind: OpKind.PREENDORSEMENT; slot: number; @@ -155,6 +200,12 @@ export interface OperationContentsPreEndorsement { block_payload_hash: string; } +export interface OperationContentsDoublePreattestation { + kind: OpKind.DOUBLE_PREATTESTATION_EVIDENCE; + op1: InlinedPreattestation; + op2: InlinedPreattestation; +} + export interface OperationContentsDoublePreEndorsement { kind: OpKind.DOUBLE_PREENDORSEMENT_EVIDENCE; op1: InlinedPreEndorsement; @@ -171,6 +222,12 @@ export interface OperationContentsSetDepositsLimit { limit?: string; } +export interface OperationContentsAttestationWithSlot { + kind: OpKind.ATTESTATION_WITH_SLOT; + endorsement: InlinedAttestation; + slot: number; +} + export interface OperationContentsEndorsementWithSlot { kind: OpKind.ENDORSEMENT_WITH_SLOT; endorsement: InlinedEndorsement; @@ -188,6 +245,13 @@ export interface OperationContentsVdfRevelation { solution: string[]; } +export interface OperationContentsDoubleAttestation { + kind: OpKind.DOUBLE_ATTESTATION_EVIDENCE; + op1: InlinedAttestation; + op2: InlinedAttestation; + slot?: number; +} + export interface OperationContentsDoubleEndorsement { kind: OpKind.DOUBLE_ENDORSEMENT_EVIDENCE; op1: InlinedEndorsement; @@ -416,11 +480,15 @@ export interface OperationContentsSmartRollupTimeout { } export type OperationContents = + | OperationContentsAttestation + | OperationContentsPreattestation + | OperationContentsDoublePreattestation | OperationContentsEndorsement | OperationContentsPreEndorsement | OperationContentsDoublePreEndorsement | OperationContentsRevelation | OperationContentsVdfRevelation + | OperationContentsDoubleAttestation | OperationContentsDoubleEndorsement | OperationContentsDoubleBaking | OperationContentsActivateAccount @@ -430,6 +498,7 @@ export type OperationContents = | OperationContentsTransaction | OperationContentsOrigination | OperationContentsDelegation + | OperationContentsAttestationWithSlot | OperationContentsEndorsementWithSlot | OperationContentsFailingNoop | OperationContentsRegisterGlobalConstant @@ -447,7 +516,14 @@ export type OperationContents = | OperationContentsSmartRollupRecoverBond | OperationContentsSmartRollupTimeout; -export interface OperationContentsAndResultMetadataExtended { +export interface OperationContentsAndResultMetadataExtended1 { + balance_updates?: OperationMetadataBalanceUpdates[]; + delegate: string; + consensus_power: number; + consensus_key: string; +} + +export interface OperationContentsAndResultMetadataExtended0 { balance_updates?: OperationMetadataBalanceUpdates[]; delegate: string; slots?: number[]; @@ -455,6 +531,13 @@ export interface OperationContentsAndResultMetadataExtended { consensus_key?: string; } +export interface OperationContentsAndResultMetadataPreattestation { + balance_updates?: OperationMetadataBalanceUpdates[]; + delegate: string; + consensus_power: number; + consensus_key?: string; +} + export interface OperationContentsAndResultMetadataPreEndorsement { balance_updates?: OperationMetadataBalanceUpdates[]; delegate: string; @@ -567,13 +650,31 @@ export interface OperationContentsAndResultMetadataSmartRollupTimeout { internal_operation_results?: InternalOperationResult[]; } +export interface OperationContentsAndResultAttestation { + kind: OpKind.ATTESTATION; + block_payload_hash?: string; + level: number; + round?: number; + slot?: number; + metadata: OperationContentsAndResultMetadataExtended1; +} + export interface OperationContentsAndResultEndorsement { kind: OpKind.ENDORSEMENT; block_payload_hash?: string; level: number; round?: number; slot?: number; - metadata: OperationContentsAndResultMetadataExtended; + metadata: OperationContentsAndResultMetadataExtended0; +} + +export interface OperationContentsAndResultPreattestation { + kind: OpKind.PREATTESTATION; + slot: number; + level: number; + round: number; + block_payload_hash: string; + metadata: OperationContentsAndResultMetadataPreattestation; } export interface OperationContentsAndResultPreEndorsement { @@ -585,17 +686,32 @@ export interface OperationContentsAndResultPreEndorsement { metadata: OperationContentsAndResultMetadataPreEndorsement; } +export interface OperationContentsAndResultDoublePreattestation { + kind: OpKind.DOUBLE_PREATTESTATION_EVIDENCE; + op1: InlinedPreattestation; + op2: InlinedPreattestation; + metadata: OperationContentsAndResultMetadata; +} + +export interface OperationContentsAndResultAttestationWithSlot { + kind: OpKind.ATTESTATION_WITH_SLOT; + endorsement: InlinedAttestation; + slot: number; + metadata: OperationContentsAndResultMetadataExtended1; +} + export interface OperationContentsAndResultDoublePreEndorsement { kind: OpKind.DOUBLE_PREENDORSEMENT_EVIDENCE; op1: InlinedPreEndorsement; op2: InlinedPreEndorsement; metadata: OperationContentsAndResultMetadata; } + export interface OperationContentsAndResultEndorsementWithSlot { kind: OpKind.ENDORSEMENT_WITH_SLOT; endorsement: InlinedEndorsement; slot: number; - metadata: OperationContentsAndResultMetadataExtended; + metadata: OperationContentsAndResultMetadataExtended0; } export interface OperationContentsAndResultRevelation { @@ -605,6 +721,14 @@ export interface OperationContentsAndResultRevelation { metadata: OperationContentsAndResultMetadata; } +export interface OperationContentsAndResultDoubleAttestation { + kind: OpKind.DOUBLE_ATTESTATION_EVIDENCE; + op1: InlinedAttestation; + op2: InlinedAttestation; + slot?: number; + metadata: OperationContentsAndResultMetadata; +} + export interface OperationContentsAndResultDoubleEndorsement { kind: OpKind.DOUBLE_ENDORSEMENT_EVIDENCE; op1: InlinedEndorsement; @@ -851,11 +975,15 @@ export interface OperationContentsAndResultSmartRollupTimeout { } export type OperationContentsAndResult = + | OperationContentsAndResultAttestation + | OperationContentsAndResultPreattestation + | OperationContentsAndResultDoublePreattestation | OperationContentsAndResultEndorsement | OperationContentsAndResultPreEndorsement | OperationContentsAndResultDoublePreEndorsement | OperationContentsAndResultRevelation | OperationContentsAndResultDoubleEndorsement + | OperationContentsAndResultDoubleAttestation | OperationContentsAndResultDoubleBaking | OperationContentsAndResultActivateAccount | OperationContentsAndResultProposals @@ -864,6 +992,7 @@ export type OperationContentsAndResult = | OperationContentsAndResultTransaction | OperationContentsAndResultOrigination | OperationContentsAndResultDelegation + | OperationContentsAndResultAttestationWithSlot | OperationContentsAndResultEndorsementWithSlot | OperationContentsAndResultRegisterGlobalConstant | OperationContentsAndResultSetDepositsLimit @@ -943,6 +1072,34 @@ export interface BakingRightsResponseItem { export type BakingRightsResponse = BakingRightsResponseItem[]; +export type AttestationRightsArgumentsDelegate = string | string[]; +export type AttestationRightsArgumentsCycle = number | number[]; +export type AttestationRightsArgumentsLevel = number | number[]; + +export interface AttestationRightsQueryArguments { + level?: AttestationRightsArgumentsLevel; + cycle?: AttestationRightsArgumentsCycle; + delegate?: AttestationRightsArgumentsDelegate; + consensus_key?: string; +} + +export interface AttestationRightsResponseItemDelegates { + delegate: string; + first_slot: number; + attestation_power: number; + consensus_key: string; +} + +export interface AttestationRightsResponseItem { + level: number; + delegate?: string; + delegates?: AttestationRightsResponseItemDelegates[]; + slots?: number[]; + estimated_time?: Date; +} + +export type AttestationRightsResponse = AttestationRightsResponseItem[]; + export type EndorsingRightsArgumentsDelegate = string | string[]; export type EndorsingRightsArgumentsCycle = number | number[]; export type EndorsingRightsArgumentsLevel = number | number[]; @@ -1717,14 +1874,15 @@ export interface ConstantsResponseProto015 export interface DalParametric { feature_enable: boolean; number_of_slots: number; + attestation_lag: number; + attestation_threshold?: number; + blocks_per_epoch?: number; + redundancy_factor?: number; + page_size?: number; + slot_size?: number; number_of_shards: number; endorsement_lag?: number; availability_threshold: number; - slot_size?: number; - redundancy_factor?: number; - page_size?: number; - attestation_threshold?: number; - blocks_per_epoch?: number; } export interface ConstantsResponseProto014 extends ConstantsResponseProto013 { diff --git a/packages/taquito-rpc/test/data/rpc-responses.ts b/packages/taquito-rpc/test/data/rpc-responses.ts index e9128d3f22..7f72c42738 100644 --- a/packages/taquito-rpc/test/data/rpc-responses.ts +++ b/packages/taquito-rpc/test/data/rpc-responses.ts @@ -2446,6 +2446,19 @@ export const blockMetadata = { }, ], }; +export const attestationRights = [ + { + level: 151187, + delegates: [ + { + delegate: 'tz3Q1fwk1vh3zm5LqyUV9e2wZBdaEXcovh2r', + first_slot: 79, + attestation_power: 326, + consensus_key: 'tz3Q1fwk1vh3zm5LqyUV9e2wZBdaEXcovh2r', + }, + ], + }, +]; export const endorsingRights = [ { level: 516500, diff --git a/packages/taquito-rpc/test/rpc-cache.spec.ts b/packages/taquito-rpc/test/rpc-cache.spec.ts index 26d72b76b5..9048af5407 100644 --- a/packages/taquito-rpc/test/rpc-cache.spec.ts +++ b/packages/taquito-rpc/test/rpc-cache.spec.ts @@ -15,6 +15,7 @@ import { blockHeader, blockMetadata, bakingRights, + attestationRights, endorsingRights, ballotList, ballots, @@ -71,6 +72,7 @@ describe('RpcClientCache test', () => { getBlockHeader: jest.fn(), getBlockMetadata: jest.fn(), getBakingRights: jest.fn(), + getAttestationRights: jest.fn(), getEndorsingRights: jest.fn(), getBallotList: jest.fn(), getBallots: jest.fn(), @@ -110,6 +112,7 @@ describe('RpcClientCache test', () => { mockRpcClient.getBlockHeader.mockReturnValue(blockHeader); mockRpcClient.getBlockMetadata.mockReturnValue(blockMetadata); mockRpcClient.getBakingRights.mockReturnValue(bakingRights); + mockRpcClient.getAttestationRights.mockReturnValue(attestationRights); mockRpcClient.getEndorsingRights.mockReturnValue(endorsingRights); mockRpcClient.getBallotList.mockReturnValue(ballotList); mockRpcClient.getBallots.mockReturnValue(ballots); @@ -154,6 +157,7 @@ describe('RpcClientCache test', () => { await rpcCache.getBlockHeader(); await rpcCache.getBlockMetadata(); await rpcCache.getBakingRights(); + await rpcCache.getAttestationRights(); await rpcCache.getEndorsingRights(); await rpcCache.getBallotList(); await rpcCache.getBallots(); @@ -228,6 +232,9 @@ describe('RpcClientCache test', () => { expect(rpcCache.getAllCachedData()['rpcTest/getBakingRights/head/{}/'].response).toEqual( bakingRights ); + expect(rpcCache.getAllCachedData()['rpcTest/getAttestationRights/head/{}/'].response).toEqual( + attestationRights + ); expect(rpcCache.getAllCachedData()['rpcTest/getEndorsingRights/head/{}/'].response).toEqual( endorsingRights ); @@ -298,6 +305,7 @@ describe('RpcClientCache test', () => { await rpcCache.getBlockHeader(block); await rpcCache.getBlockMetadata(block); await rpcCache.getBakingRights({ level: 1111 }, block); + await rpcCache.getAttestationRights({ level: 151187 }, block); await rpcCache.getEndorsingRights({ level: 1111 }, block); await rpcCache.getBallotList(block); await rpcCache.getBallots(block); @@ -386,6 +394,10 @@ describe('RpcClientCache test', () => { expect( rpcCache.getAllCachedData()[`rpcTest/getBakingRights/${block.block}/{"level":1111}/`].response ).toEqual(bakingRights); + expect( + rpcCache.getAllCachedData()[`rpcTest/getAttestationRights/${block.block}/{"level":151187}/`] + .response + ).toEqual(attestationRights); expect( rpcCache.getAllCachedData()[`rpcTest/getEndorsingRights/${block.block}/{"level":1111}/`] .response @@ -457,6 +469,7 @@ describe('RpcClientCache test', () => { await rpcCache.getBlockHeader(); await rpcCache.getBlockMetadata(); await rpcCache.getBakingRights(); + await rpcCache.getAttestationRights(); await rpcCache.getEndorsingRights(); await rpcCache.getBallotList(); await rpcCache.getBallots(); diff --git a/packages/taquito-utils/src/verify-signature.ts b/packages/taquito-utils/src/verify-signature.ts index 658a0cb867..3b8560aafb 100644 --- a/packages/taquito-utils/src/verify-signature.ts +++ b/packages/taquito-utils/src/verify-signature.ts @@ -23,7 +23,7 @@ type SigPrefix = Prefix.EDSIG | Prefix.SPSIG | Prefix.P2SIG | Prefix.SIG; * @description Verify signature of a payload * * @param messageBytes The forged message including the magic byte (11 for block, - * 12 for preendorsement, 13 for endorsement, 3 for generic, 5 for the PACK format of michelson) + * 12 for preattestation/preendorsement, 13 for attestation/endorsement, 3 for generic, 5 for the PACK format of michelson) * @param publicKey The public key to verify the signature against * @param signature The signature to verify * @returns A boolean indicating if the signature matches diff --git a/packages/taquito/README.md b/packages/taquito/README.md index 40764b02b0..817a4eb5ca 100644 --- a/packages/taquito/README.md +++ b/packages/taquito/README.md @@ -1,4 +1,4 @@ -# Taquito high-level functions +# Taquito high-level functions *TypeDoc style documentation is available on-line [here](https://tezostaquito.io/typedoc/modules/_taquito_taquito.html)* @@ -13,9 +13,9 @@ crossorigin="anonymous" integrity="sha384-IxvP0ECHi5oqLyz94wF85pU9+ktcsL1HHtA42M ## General Information -The `TezosToolkit` is a facade class that surfaces all of the library's capability and allows its configuration through different providers. +The `TezosToolkit` is a facade class that surfaces all of the library's capability and allows its configuration through different providers. -## Install +## Install ``` npm i --save @taquito/taquito @@ -25,7 +25,7 @@ npm i --save @taquito/taquito ## Minimal configuration ### TezosToolkit instantiation -The `TezosToolkit` constructor takes at least an RPC URL as a parameter. When instantiating the toolkit with a URL, a default instance of `RpcClient` is created. The `RpcClient` class is used to interact with the Tezos network. +The `TezosToolkit` constructor takes at least an RPC URL as a parameter. When instantiating the toolkit with a URL, a default instance of `RpcClient` is created. The `RpcClient` class is used to interact with the Tezos network. ```ts import { TezosToolkit } from '@taquito/taquito'; @@ -114,7 +114,7 @@ Tezos.setProvider( ## Estimation -Use the `estimate` member to estimate fees, gas and storage of operations. +Use the `estimate` member to estimate fees, gas and storage of operations. ```ts const estimate = await Tezos.estimate.transfer(transferParams); @@ -125,12 +125,12 @@ const estimate = await Tezos.estimate.transfer(transferParams); Use the `stream` member to subscribe to specific operations: ```ts -Tezos.setProvider({ - config: { shouldObservableSubscriptionRetry: true, streamerPollingIntervalMilliseconds: 15000 } +Tezos.setProvider({ + config: { shouldObservableSubscriptionRetry: true, streamerPollingIntervalMilliseconds: 15000 } }); -const bakerEndorsementFilter = { - and: [{ source: 'tz2TSvNTh2epDMhZHrw73nV9piBX7kLZ9K9m' }, { kind: 'endorsement' }] +const bakerAttestationFilter = { + and: [{ source: 'tz2TSvNTh2epDMhZHrw73nV9piBX7kLZ9K9m' }, { kind: 'attestation' }] } const bakerDelegation = { @@ -138,7 +138,7 @@ const bakerDelegation = { } const sub = tezos.stream.subscribeOperation({ - or: [bakerEndorsementFilter, bakerDelegation] + or: [bakerAttestationFilter, bakerDelegation] }) sub.on('data', console.log) diff --git a/packages/taquito/src/signer/interface.ts b/packages/taquito/src/signer/interface.ts index 980de2eddd..9416c4a7e4 100644 --- a/packages/taquito/src/signer/interface.ts +++ b/packages/taquito/src/signer/interface.ts @@ -5,7 +5,7 @@ export interface Signer { /** * * @param op Operation to sign - * @param magicByte Magic bytes 11 for block, 12 for preendorsement, 13 for endorsements, 3 for generic, 5 for the PACK format of michelson + * @param magicByte Magic bytes 11 for block, 12 for preattestation/preendorsement, 13 for attestation/endorsements, 3 for generic, 5 for the PACK format of michelson */ sign( op: string, diff --git a/packages/taquito/src/subscribe/filters.ts b/packages/taquito/src/subscribe/filters.ts index eafc9db514..b2c95baa60 100644 --- a/packages/taquito/src/subscribe/filters.ts +++ b/packages/taquito/src/subscribe/filters.ts @@ -17,6 +17,8 @@ const opHashFilter = (op: OperationContent, filter: OpHashFilter) => op.hash === const sourceFilter = (x: OperationContent, filter: SourceFilter) => { switch (x.kind) { + case 'attestation': + return 'metadata' in x && x.metadata.delegate === filter.source; case 'endorsement': return 'metadata' in x && x.metadata.delegate === filter.source; case 'activate_account':