# All files / lib/messages OracleAnnouncement.ts

93.02% Statements 40/43
56.25% Branches 9/16
88.89% Functions 8/9
93.02% Lines 40/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    1771x
    1x

    1x
    1x

    1x

    1x
    1x

    148x

    148x

    148x

    148x

    148x

    248x
    248x

    248x
    248x
    248x
    248x
    248x

    248x

    451x

    13x

    12x

    12x

    12x
    12x

    12x

    1x

    3x

    16x

    81x

    670x
    670x

    670x
    670x
    670x
    670x

    670x
    670x

    670x

     import { BufferReader, BufferWriter } from '@node-dlc/bufio';
    import { math, verify } from 'bip-schnorr';

    import { MessageType } from '../MessageType';
    import { getTlv } from '../serialize/getTlv';
    import { IDlcMessage } from './DlcMessage';
    import { IOracleEventJSON, OracleEvent } from './OracleEvent';

    /**
     * Oracle announcement that describe an event and the way that an oracle will
     * attest to it. Updated to be rust-dlc compliant.
     *
     * In order to make it possible to hold oracles accountable in cases where
     * they do not release a signature for an event outcome, there needs to be
     * a proof that an oracle has committed to a given outcome. This proof is
     * given in a so-called oracle announcement, which contains an oracle event
     * together with the oracle public key and a signature over its serialization,
     * which must be valid with respect to the specified public key.
     *
     * This also makes it possible for users to obtain oracle event information
     * from an un-trusted peer while being guaranteed that it originates from a
     * given oracle.
     */
    export class OracleAnnouncement implements IDlcMessage {
      public static type = MessageType.OracleAnnouncement;

      /**
       * Creates an OracleAnnouncement from JSON data
       * @param json JSON object representing oracle announcement
       */
      // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
      public static fromJSON(json: any): OracleAnnouncement {
        const instance = new OracleAnnouncement();

        // Handle different field name variations
        instance.announcementSig = Buffer.from(
          json.announcementSignature ||
            json.announcementSig ||
            json.announcement_signature,
          'hex',
        );
        instance.oraclePubkey = Buffer.from(
          json.oraclePublicKey || json.oraclePubkey || json.oracle_public_key,
          'hex',
        );
        instance.oracleEvent = OracleEvent.fromJSON(
          json.oracleEvent || json.oracle_event,
        );

        return instance;
      }

      /**
       * Deserializes an oracle_announcement message
       * @param buf
       */
      public static deserialize(buf: Buffer): OracleAnnouncement {
        const instance = new OracleAnnouncement();
        const reader = new BufferReader(buf);

        reader.readBigSize(); // read type
        instance.length = reader.readBigSize();
        instance.announcementSig = reader.readBytes(64);
        instance.oraclePubkey = reader.readBytes(32);
        instance.oracleEvent = OracleEvent.deserialize(getTlv(reader));

        return instance;
      }

      /**
       * The type for oracle_announcement message. oracle_announcement = 55332
       */
      public type = OracleAnnouncement.type;

      public length: bigint;

      /** The signature enabling verifying the origin of the announcement. */
      public announcementSig: Buffer;

      /** The public key of the oracle (32 bytes, x-only). */
      public oraclePubkey: Buffer;

      /** The description of the event and attesting. */
      public oracleEvent: OracleEvent;

      /**
       * Validates the oracle announcement according to rust-dlc specification.
       * This includes validating the oracle event and verifying the announcement signature.
       * @throws Will throw an error if validation fails
       */
      public validate(): void {
        // Validate oracle event first
        this.oracleEvent.validate();

        // Validate oracle public key format (32 bytes for x-only)
        Iif (!this.oraclePubkey || this.oraclePubkey.length !== 32) {
          throw new Error('Oracle public key must be 32 bytes (x-only format)');
        }

        // Validate announcement signature format (64 bytes for Schnorr)
        Iif (!this.announcementSig || this.announcementSig.length !== 64) {
          throw new Error(
            'Announcement signature must be 64 bytes (Schnorr format)',
          );
        }

        // Verify announcement signature over the oracle event
        try {
          const msg = math.taggedHash(
            'DLC/oracle/announcement/v0',
            this.oracleEvent.serialize(),
          );
          verify(this.oraclePubkey, msg, this.announcementSig);
        } catch (error) {
          throw new Error(`Invalid announcement signature: ${error.message}`);
        }
      }

      /**
       * Returns the nonces from the oracle event.
       * This is useful for finding matching oracle announcements.
       */
      public getNonces(): Buffer[] {
        return this.oracleEvent.oracleNonces;
      }

      /**
       * Returns the event maturity epoch from the oracle event.
       */
      public getEventMaturityEpoch(): number {
        return this.oracleEvent.eventMaturityEpoch;
      }

      /**
       * Returns the event ID from the oracle event.
       */
      public getEventId(): string {
        return this.oracleEvent.eventId;
      }

      /**
       * Converts oracle_announcement to JSON (canonical rust-dlc format)
       */
      public toJSON(): OracleAnnouncementJSON {
        return {
          announcementSignature: this.announcementSig.toString('hex'),
          oraclePublicKey: this.oraclePubkey.toString('hex'),
          oracleEvent: this.oracleEvent.toJSON(),
        };
      }

      /**
       * Serializes the oracle_announcement message into a Buffer
       */
      public serialize(): Buffer {
        const writer = new BufferWriter();
        writer.writeBigSize(this.type);

        const dataWriter = new BufferWriter();
        dataWriter.writeBytes(this.announcementSig);
        dataWriter.writeBytes(this.oraclePubkey);
        dataWriter.writeBytes(this.oracleEvent.serialize());

        writer.writeBigSize(dataWriter.size);
        writer.writeBytes(dataWriter.toBuffer());

        return writer.toBuffer();
      }
    }

    export interface OracleAnnouncementJSON {
      type?: number; // Made optional for rust-dlc compatibility
      announcementSignature: string; // Canonical field name
      oraclePublicKey: string; // Canonical field name
      oracleEvent: IOracleEventJSON;
    }