Eşleme

... 2022-8-5 About 6 min

# Eşleme

Eşleme işlevleri, zincir verilerinin daha önce schema.graphql dosyasında tanımladığımız optimize edilmiş GraphQL varlıklarına nasıl dönüştürüleceğini tanımlar.

  • Eşlemeler src/mappings dizininde tanımlanır ve işlev olarak verilir
  • Bu eşlemeler ayrıca src/index.ts olarak da verilir
  • Eşleme dosyaları, eşleme işleyicileri altında project.yaml başvurudur.

Eşleme işlevlerinin üç sınıfı vardır; Block handlers , Event Handlers ve Call Handlers.

# Blok işleyicisi

Alt tabaka zincirine her yeni blok numarası bilgi yakalamak için blok işleyicilerini kullanabilirsiniz. Bunu başarmak için, tanımlanan bir BlockHandler her blok için bir kez çağrılır.

import {SubstrateBlock} from "@subql/types";

export async function handleBlock(block: SubstrateBlock): Promise<void> {
    // Create a new StarterEntity with the block hash as it's ID
    const record = new starterEntity(block.block.header.hash.toString());
    record.field1 = block.block.header.number.toNumber();
    await record.save();
}
1
2
3
4
5
6
7
8

SubstrateBlock (opens new window), signedBlock (opens new window)’un genişletilmiş bir arabirim türüdür, ancak specVersion ve timestamp da içerir.

# Olay İşleyicisi

Belirli olaylar yeni bir bloğa eklendiğinde bilgi yakalamak için olay işleyicilerini kullanabilirsiniz. Varsayılan substrat çalışma zamanının ve bir bloğun parçası olan olaylar birden çok olay içerebilir.

Olay işleyici, işleme sırasında olayın yazılan giriş ve çıkışlarıyla bir argüman olarak bir substrat olayı alacaktır. Her türlü olay eşlemeyi tetikleyerek veri kaynağıyla etkinliğin yakalanmasına izin verir. Verileri indekslemek ve eşleme performansını artırmak için gereken süreyi azaltmak üzere olayları filtrelemek için bildirininizde Mapping Filters kullanmalısınız.

mport {SubstrateEvent} from "@subql/types";

export async function handleEvent(event: SubstrateEvent): Promise<void> {
    const {event: {data: [account, balance]}} = event;
    // Retrieve the record by its ID
    const record = new starterEntity(event.extrinsic.block.block.header.hash.toString());
    record.field2 = account.toString();
    record.field3 = (balance as Balance).toBigInt();
    await record.save();
1
2
3
4
5
6
7
8
9

SubstratEvent (opens new window), EventRecord (opens new window) ‘un genişletilmiş arabirim türüdür. Olay verilerinin yanı sıra, id (bu olayın ait olduğu blok) ve bu bloğun içindeki dış öğeyi de içerir.

# Çağrı Işleyicisi

Çağrı işleyicileri, belirli substrat dış değerleri hakkında bilgi yakalamak istediğinizde kullanılır.

export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> {
    const record = new starterEntity(extrinsic.block.block.header.hash.toString());
    record.field4 = extrinsic.block.timestamp;
    await record.save();
}
1
2
3
4
5

SubstrateExtrinsic (opens new window) GenericExtrinsic (opens new window)’i genişletir. Bir id (bu dışsal öğenin ait olduğu blok) atanır ve olayları bu blok arasında genişleten dışsal bir özellik sağlar. Ayrıca, bu dışsal başarı durumunu kaydeder.

# Sorgu Durumları

Amacımız, işleyicileri eşlemek için kullanıcılar için tüm veri kaynaklarını kapsamaktır (yukarıdaki üç arabirim olay türünden daha fazlası). Bu nedenle, yetenekleri artırmak için bazı @polkadot/api arabirimlerini kullanıma açtık.

Şu anda desteklediğimiz arayüzler şunlardır:

Şu anda desteklediğimiz NOT arabirimler şunlardır:

  • api.tx.*
  • api.derive.*
  • api.query.<module>.<method>.at
  • api.query.<module>.<method>.entriesAt
  • api.query.<module>.<method>.entriesPaged
  • api.query.<module>.<method>.hash
  • ~~api.query. <module>. <method>.keys At ~~
  • api.query.<module>.<method>.keysPaged
  • api.query.<module>.<method>.range
  • api.query.<module>.<method>.sizeAt

Bu API'nin örnek kullanımını validator-threshold (opens new window) örneğimizde görebilirsiniz.

# RPC çağrıları

Ayrıca, eşleme işlevinin gerçek düğüm, sorgu ve gönderim ile etkileşime girmesine izin veren uzaktan çağrılar olan bazı API RPC yöntemlerini de destekliyoruz. SubQuery'nin temel dayanağı deterministik olmasıdır ve bu nedenle sonuçları tutarlı tutmak için yalnızca geçmiş RPC çağrılarına izin veririz.

JSON-RPC (opens new window)’deki belgeler, BlockHash ‘i giriş parametresi olarak alan bazı yöntemler sağlar, (örn. at?: BlockHash) ki bunlara izin verilmez. Bu yöntemleri, varsayılan olarak geçerli indeksleyici bloğu karma işlevini alacak şekilde de değiştirdik.

// Diyelim ki şu anda bu karma numaraya sahip bir bloğu dizine ekleniyoruz
const blockhash = `0x844047c4cf1719ba6d54891e92c071a41e3dfe789d064871148e9d41ef086f6a`;

// Özgün yöntemin isteğe bağlı girişi vardır: blok karma
const b1 = await api.rpc.chain.getBlock(blockhash);

// Geçerli bloğun varsayılan olarak böyle olduğunu kullanır
const b2 = await api.rpc.chain.getBlock();
1
2
3
4
5
6
7
8

# Modüller ve Kitaplıklar

SubQuery'nin veri işleme yeteneklerini geliştirmek için, NodeJS'in sandbox’daki eşleme işlevlerini çalıştırmak için yerleşik modüllerinden bazılarına ve kullanıcıların üçüncü taraf kitaplıkları aramasına izin verdik.

Bunun bir experimental feature olduğunu ve eşleme işlevlerinizi olumsuz etkileyebilecek hatalarla veya sorunlarla karşılaşabileceğinizi lütfen unutmayın. Lütfen bir sorun oluşturarak bulduğunuz hataları GitHub (opens new window)’ta bildirin.

# Yerleşik modüller

Şu anda, aşağıdaki NodeJS modüllerine izin veriyoruz: assert, buffer, crypto, util, ve path.

Modülün tamamını almak yerine, yalnızca ihtiyacınız olan gerekli yöntemleri almanızı öneririz. Bu modüllerdeki bazı yöntemlerin desteklenmeyen bağlılıkları olabilir ve içe aktarma işlemi başarısız olur.

import {hashMessage} from "ethers/lib/utils"; //Good way
import {utils} from "ethers" //Bad way

export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> {
    const record = new starterEntity(extrinsic.block.block.header.hash.toString());
    record.field1 = hashMessage('Hello');
    await record.save();
}
1
2
3
4
5
6
7
8

# Üçüncü taraf kitaplıkları

Sandbox’umuzdaki sanal makinenin sınırlamaları nedeniyle, şu anda yalnızca CommonJS tarafından yazılmış üçüncü taraf kitaplıkları destekliyoruz.

Ayrıca, varsayılan olarak ESM kullanan @polkadot/* gibi hybrid kitaplığını da destekliyoruz. Ancak, başka kitaplıklar ESM biçimindeki modüllere bağlıysa, sanal makine DERLENMEZ ve bir hata döndürür.

# Özel Substrat Zincirleri

SubQuery, sadece Polkadot veya Kusama'da değil, Substrat tabanlı herhangi bir zincirde kullanılabilir.

Özel bir Substrat tabanlı zincir kullanabilirsiniz ve türleri, arayüzleri ve ek yöntemleri @polkadot/typegen (opens new window) kullanarak otomatik olarak içe aktarmak için araçlar sağlarız.

Aşağıdaki bölümlerde, entegrasyon sürecini açıklamak için kitty example (opens new window) kullanıyoruz.

# Hazırlık

Gerekli ve oluşturulan tüm dosyaları depolamak için proje src klasörü altında yeni bir dizin api-interfaces oluşturun. Ayrıca, kitties modülünden API'ye dekorasyon eklemek istediğimiz için api-interfaces/kitties dizini oluşturuyoruz.

# Meta veriler

Gerçek API uç noktalarını oluşturmak için meta verilere ihtiyacımız var. Kitty örneğinde, yerel bir testnet’ten bir uç nokta kullanıyoruz ve bu ek türler sağlıyor. Bir düğümün meta verilerini HTTP uç noktasından almak için PolkadotJS metadata setup (opens new window)'daki adımları izleyin.

curl -H "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933
1

websocat (opens new window) yardımıyla websocket uç noktasından:

//Websocat'i yükleme
brew install websocat

//Meta verileri alma
echo state_getMetadata | websocat 'ws://127.0.0.1:9944' --jsonrpc
1
2
3
4
5

Ardından, çıktıyı kopyalayıp bir JSON dosyasına yapıştırın. kitty örneğimizde (opens new window) api-interface/kitty.json oluşturduk.

# Tür tanımları

Kullanıcının zincirden belirli türleri ve RPC desteğini bildiğini ve Manifest tanımlandığını varsayıyoruz.

types setup (opens new window)’nu izleyerek aşağıdakileri oluşturuyoruz:

  • src/api-interfaces/definitions.ts - bu, tüm alt klasör tanımlarını dışa aktarıyor
export { default as kitties } from './kitties/definitions';
1
  • src/api-interfaces/kitties/definitions.ts - kitties modülü için tür tanımları
export default {
    // custom types
    types: {
        Address: "AccountId",
        LookupSource: "AccountId",
        KittyIndex: "u32",
        Kitty: "[u8; 16]"
    },
    // custom rpc : api.rpc.kitties.getKittyPrice
    rpc: {
        getKittyPrice:{
            description: 'Get Kitty price',
            params: [
                {
                    name: 'at',
                    type: 'BlockHash',
                    isHistoric: true,
                    isOptional: false
                },
                {
                    name: 'kittyIndex',
                    type: 'KittyIndex',
                    isOptional: false
                }
            ],
            type: 'Balance'
        }
    }
}
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

# Paket

  • package.json dosyasına, geliştirme bağımlılığı olarak @polkadot/typegen ve normal bir bağımlılık olarak @polkadot/api eklediğinizden emin olun ( ideal olarak aynı sürüm). Ayrıca, komut dosyalarını çalıştırmamıza yardımcı olmak için geliştirme bağımlılığı olarak ts-node ihtiyacımız vardır.
  • Her iki türü de çalıştırmak için komut dosyaları ekliyoruz; generate:defs ve meta veri generate:meta üreteçleri (bu sırada, meta veriler türleri kullanabilir).

İşte package.json’un basitleştirilmiş bir sürümü. scripts bölümünde paket adının doğru olduğundan ve dizinlerin geçerli olduğundan emin olun.

{
  "name": "kitty-birthinfo",
  "scripts": {
    "generate:defs": "ts-node --skip-project node_modules/.bin/polkadot-types-from-defs --package kitty-birthinfo/api-interfaces --input ./src/api-interfaces",
    "generate:meta": "ts-node --skip-project node_modules/.bin/polkadot-types-from-chain --package kitty-birthinfo/api-interfaces --endpoint ./src/api-interfaces/kitty.json --output ./src/api-interfaces --strict"
  },
  "dependencies": {
    "@polkadot/api": "^4.9.2"
  },
  "devDependencies": {
    "typescript": "^4.1.3",
    "@polkadot/typegen": "^4.9.2",
    "ts-node": "^8.6.2"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Tür oluşturma

Hazırlık tamamlandıktan sonra, türler ve meta veriler oluşturmaya hazırız. Aşağıdaki komutları çalıştırın:

# Yeni bağımlılıklar yüklemek için iplik
yarn

# Türler oluştur
yarn generate:defs
1
2
3
4
5

Her modül klasöründe (örneğin /kitties), artık bu modüllerin tanımlarından tüm arayüzleri tanımlayan, oluşturulmuş bir types.ts olmalıdır, ayrıca hepsini dışa aktaran index.ts dosyası olmalıdır.

# Meta veriler oluştur
yarn generate:meta
1
2

Bu komut meta verileri ve API'ler için yeni bir api-augment oluşturur. Yerleşik API'yi kullanmak istemediğimiz için, tsconfig.json’e açık bir geçersiz kılma ekleyerek bunları değiştirmemiz gerekecektir. Güncelleştirmelerden sonra, yapılandırmadaki yollar şöyle görünecektir (açıklamalar olmadan):

{
  "compilerOptions": {
      // Bu, kullandığımız paket adıdır (arayüz içe aktarmalarında, --jeneratörler için paket) */
      "kitty-birthinfo/*": ["src/*"],
     // Burada @polkadot/api büyütmeyi zincirden oluşturulan kendi büyütmemizle değiştiriyoruz
      "@polkadot/api/augment": ["src/interfaces/augment-api.ts"],
     // tanımlardan oluşturulan artırılmış türleri kendi türlerimizle değiştirin
      "@polkadot/türleri/büyütme": ["src/interfaces/augment-types.ts"]
    }
}
1
2
3
4
5
6
7
8
9
10

# Kullanım

Şimdi eşleme işlevinde, meta verilerin ve türlerin API'yi gerçekte nasıl dekore ederek süslediğini gösterebiliriz. RPC uç noktası yukarıda beyan ettiğimiz modülleri ve yöntemleri destekleyecektir. Ve özel rpc çağrısı kullanmak için, lütfen Özel zincir rpc çağrıları bölümüne bakın

export async function kittyApiHandler(): Promise<void> {
    //KittyIndex türünü döndürme
    const nextKittyId = await api.query.kitties.nextKittyId();
   //Kitty türünü döndürür, giriş parametreleri türleri AccountId ve KittyIndex'tir
    const allKitties  = await api.query.kitties.kitties('xxxxxxxxx',123)
    logger.info(`Next kitty id ${nextKittyId}`)
    //Özel rpc, tanımsız olarak blockhash olarak ayarla
    const kittyPrice = await api.rpc.kitties.getKittyPrice(undefined,nextKittyId);
}
1
2
3
4
5
6
7
8
9

Bu projeyi gezginimize yayınlamak istiyorsanız, lütfen oluşturulan dosyaları src/api-interfaces’a ekleyin.

# Özel zincir rpc çağrıları

Özelleştirilmiş zincir RPC çağrılarını desteklemek için, typesBundle için RPC tanımlarını el ile eklemeli ve her belirti için yapılandırmaya izin vermeliyiz. typesBundle’ı project.yml’de tanımlayabilirsiniz. Ve lütfen yalnızca isHistoric tür çağrıların desteklendiğini unutmayın.

...
  types: {
    "KittyIndex": "u32",
    "Kitty": "[u8; 16]",
  }
  typesBundle: {
    spec: {
      chainname: {
        rpc: {
          kitties: {
            getKittyPrice:{
                description: string,
                params: [
                  {
                    name: 'at',
                    type: 'BlockHash',
                    isHistoric: true,
                    isOptional: false
                  },
                  {
                    name: 'kittyIndex',
                    type: 'KittyIndex',
                    isOptional: false
                  }
                ],
                type: "Balance",
            }
          }
        }
      }
    }
  }

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
Last update: August 5, 2022 10:08