锯齿波验证器未注册自定义交易处理器。记录 'No Handler for that type' 消息

Sawtooth validator is not registering a custom transaction processor. Logs 'No Handler for that type' message

我正在尝试为锯齿波构建自定义事务处理器,但我 运行 撞墙了,从那以后就一直卡在那里。我在本地机器 (using this guide) 上使用 docker 创建了一个锯齿测试网络。为了测试我的自定义事务处理器,我修改了 docker 组合文件以将 validator-0 和 rest-api-0 的端口发布到主机。我已经尝试从浏览器访问其余 api 并且它工作正常。但是,当我尝试 运行 我的自定义事务处理器时出现问题。验证者终端上的日志显示如下消息:

sawtooth-validator-default-0 | [2022-03-23 13:17:21.492 INFO     dispatch] received a message of type TP_REGISTER_REQUEST from d9de3f928215c306cf719fba501bae475c209cb41cad6efd55b410d7918dfe657f0f9211580c8b3249baec3aaf87d0fc84b58aee1c1501b8c65a7c8fc1ad14f8 but have no handler for that type

修改后的docker合成文件如下

# Copyright 2019 Cargill Incorporated
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

version: '3.6'

volumes:
  pbft-shared:

services:

# -------------=== rest api ===-------------

  rest-api-0:
    image: hyperledger/sawtooth-rest-api:nightly
    container_name: sawtooth-rest-api-default-0
    # expose:
    #   - 8008
    ports:
        - 8008:8008
    command: |
      bash -c "
        sawtooth-rest-api \
          --connect tcp://validator-0:4004 \
          --bind rest-api-0:8008
      "
    stop_signal: SIGKILL

  rest-api-1:
    image: hyperledger/sawtooth-rest-api:nightly
    container_name: sawtooth-rest-api-default-1
    expose:
      - 8008
    command: |
      bash -c "
        sawtooth-rest-api \
          --connect tcp://validator-1:4004 \
          --bind rest-api-1:8008
      "
    stop_signal: SIGKILL

  rest-api-2:
    image: hyperledger/sawtooth-rest-api:nightly
    container_name: sawtooth-rest-api-default-2
    expose:
      - 8008
    command: |
      bash -c "
        sawtooth-rest-api \
          --connect tcp://validator-2:4004 \
          --bind rest-api-2:8008
      "
    stop_signal: SIGKILL

  rest-api-3:
    image: hyperledger/sawtooth-rest-api:nightly
    container_name: sawtooth-rest-api-default-3
    expose:
      - 8008
    command: |
      bash -c "
        sawtooth-rest-api \
          --connect tcp://validator-3:4004 \
          --bind rest-api-3:8008
      "
    stop_signal: SIGKILL

  rest-api-4:
    image: hyperledger/sawtooth-rest-api:nightly
    container_name: sawtooth-rest-api-default-4
    expose:
      - 8008
    command: |
      bash -c "
        sawtooth-rest-api \
          --connect tcp://validator-4:4004 \
          --bind rest-api-4:8008
      "
    stop_signal: SIGKILL

# -------------=== shell ===-------------

  shell:
    image: hyperledger/sawtooth-shell:nightly
    container_name: sawtooth-shell-default
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        sawtooth keygen
        tail -f /dev/null
      "
    stop_signal: SIGKILL

# -------------=== validators ===-------------

  validator-0:
    image: hyperledger/sawtooth-validator:nightly
    container_name: sawtooth-validator-default-0
    # expose:
    #   - 4004
    #   - 5050
    #   - 8800
    ports:
        - "4004:4004"
        - "5050:5050"
        - "8800:8800"
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        if [ -e /pbft-shared/validators/validator-0.priv ]; then
          cp /pbft-shared/validators/validator-0.pub /etc/sawtooth/keys/validator.pub
          cp /pbft-shared/validators/validator-0.priv /etc/sawtooth/keys/validator.priv
        fi &&
        if [ ! -e /etc/sawtooth/keys/validator.priv ]; then
          sawadm keygen
          mkdir -p /pbft-shared/validators || true
          cp /etc/sawtooth/keys/validator.pub /pbft-shared/validators/validator-0.pub
          cp /etc/sawtooth/keys/validator.priv /pbft-shared/validators/validator-0.priv
        fi &&
        if [ ! -e config-genesis.batch ]; then
          sawset genesis -k /etc/sawtooth/keys/validator.priv -o config-genesis.batch
        fi &&
        while [[ ! -f /pbft-shared/validators/validator-1.pub || \
                 ! -f /pbft-shared/validators/validator-2.pub || \
                 ! -f /pbft-shared/validators/validator-3.pub || \
                 ! -f /pbft-shared/validators/validator-4.pub ]];
        do sleep 1; done
        echo sawtooth.consensus.pbft.members=\['\"'$$(cat /pbft-shared/validators/validator-0.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-1.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-2.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-3.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-4.pub)'\"'\] &&
        if [ ! -e config.batch ]; then
         sawset proposal create \
            -k /etc/sawtooth/keys/validator.priv \
            sawtooth.consensus.algorithm.name=pbft \
            sawtooth.consensus.algorithm.version=1.0 \
            sawtooth.consensus.pbft.members=\['\"'$$(cat /pbft-shared/validators/validator-0.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-1.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-2.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-3.pub)'\"','\"'$$(cat /pbft-shared/validators/validator-4.pub)'\"'\] \
            sawtooth.publisher.max_batches_per_block=1200 \
            -o config.batch
        fi &&
        if [ ! -e /var/lib/sawtooth/genesis.batch ]; then
          sawadm genesis config-genesis.batch config.batch
        fi &&
        if [ ! -e /root/.sawtooth/keys/my_key.priv ]; then
          sawtooth keygen my_key
        fi &&
        sawtooth-validator -vv \
          --endpoint tcp://validator-0:8800 \
          --bind component:tcp://eth0:4004 \
          --bind consensus:tcp://eth0:5050 \
          --bind network:tcp://eth0:8800 \
          --scheduler parallel \
          --peering static \
          --maximum-peer-connectivity 10000
      "

  validator-1:
    image: hyperledger/sawtooth-validator:nightly
    container_name: sawtooth-validator-default-1
    expose:
      - 4004
      - 5050
      - 8800
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        if [ -e /pbft-shared/validators/validator-1.priv ]; then
          cp /pbft-shared/validators/validator-1.pub /etc/sawtooth/keys/validator.pub
          cp /pbft-shared/validators/validator-1.priv /etc/sawtooth/keys/validator.priv
        fi &&
        if [ ! -e /etc/sawtooth/keys/validator.priv ]; then
          sawadm keygen
          mkdir -p /pbft-shared/validators || true
          cp /etc/sawtooth/keys/validator.pub /pbft-shared/validators/validator-1.pub
          cp /etc/sawtooth/keys/validator.priv /pbft-shared/validators/validator-1.priv
        fi &&
        sawtooth keygen my_key &&
        sawtooth-validator -vv \
          --endpoint tcp://validator-1:8800 \
          --bind component:tcp://eth0:4004 \
          --bind consensus:tcp://eth0:5050 \
          --bind network:tcp://eth0:8800 \
          --scheduler parallel \
          --peering static \
          --maximum-peer-connectivity 10000 \
          --peers tcp://validator-0:8800
      "

  validator-2:
    image: hyperledger/sawtooth-validator:nightly
    container_name: sawtooth-validator-default-2
    expose:
      - 4004
      - 5050
      - 8800
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        if [ -e /pbft-shared/validators/validator-2.priv ]; then
          cp /pbft-shared/validators/validator-2.pub /etc/sawtooth/keys/validator.pub
          cp /pbft-shared/validators/validator-2.priv /etc/sawtooth/keys/validator.priv
        fi &&
        if [ ! -e /etc/sawtooth/keys/validator.priv ]; then
          sawadm keygen
          mkdir -p /pbft-shared/validators || true
          cp /etc/sawtooth/keys/validator.pub /pbft-shared/validators/validator-2.pub
          cp /etc/sawtooth/keys/validator.priv /pbft-shared/validators/validator-2.priv
        fi &&
        sawtooth keygen my_key &&
        sawtooth-validator -vv \
          --endpoint tcp://validator-2:8800 \
          --bind component:tcp://eth0:4004 \
          --bind consensus:tcp://eth0:5050 \
          --bind network:tcp://eth0:8800 \
          --scheduler parallel \
          --peering static \
          --maximum-peer-connectivity 10000 \
          --peers tcp://validator-0:8800 \
          --peers tcp://validator-1:8800
      "

  validator-3:
    image: hyperledger/sawtooth-validator:nightly
    container_name: sawtooth-validator-default-3
    expose:
      - 4004
      - 5050
      - 8800
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        if [ -e /pbft-shared/validators/validator-3.priv ]; then
         cp /pbft-shared/validators/validator-3.pub /etc/sawtooth/keys/validator.pub
         cp /pbft-shared/validators/validator-3.priv /etc/sawtooth/keys/validator.priv
        fi &&
        if [ ! -e /etc/sawtooth/keys/validator.priv ]; then
         sawadm keygen
         mkdir -p /pbft-shared/validators || true
         cp /etc/sawtooth/keys/validator.pub /pbft-shared/validators/validator-3.pub
         cp /etc/sawtooth/keys/validator.priv /pbft-shared/validators/validator-3.priv
        fi &&
        sawtooth keygen my_key &&
        sawtooth-validator -vv \
          --endpoint tcp://validator-3:8800 \
          --bind component:tcp://eth0:4004 \
          --bind consensus:tcp://eth0:5050 \
          --bind network:tcp://eth0:8800 \
          --scheduler parallel \
          --peering static \
          --maximum-peer-connectivity 10000 \
          --peers tcp://validator-0:8800 \
          --peers tcp://validator-1:8800 \
          --peers tcp://validator-2:8800
      "

  validator-4:
    image: hyperledger/sawtooth-validator:nightly
    container_name: sawtooth-validator-default-4
    expose:
      - 4004
      - 5050
      - 8800
    volumes:
      - pbft-shared:/pbft-shared
    command: |
      bash -c "
        if [ -e /pbft-shared/validators/validator-4.priv ]; then
          cp /pbft-shared/validators/validator-4.pub /etc/sawtooth/keys/validator.pub
          cp /pbft-shared/validators/validator-4.priv /etc/sawtooth/keys/validator.priv
        fi &&
        if [ ! -e /etc/sawtooth/keys/validator.priv ]; then
          sawadm keygen
          mkdir -p /pbft-shared/validators || true
          cp /etc/sawtooth/keys/validator.pub /pbft-shared/validators/validator-4.pub
          cp /etc/sawtooth/keys/validator.priv /pbft-shared/validators/validator-4.priv
        fi &&
        sawtooth keygen my_key &&
        sawtooth-validator -vv \
          --endpoint tcp://validator-4:8800 \
          --bind component:tcp://eth0:4004 \
          --bind consensus:tcp://eth0:5050 \
          --bind network:tcp://eth0:8800 \
          --scheduler parallel \
          --peering static \
          --maximum-peer-connectivity 10000 \
          --peers tcp://validator-0:8800 \
          --peers tcp://validator-1:8800 \
          --peers tcp://validator-2:8800 \
          --peers tcp://validator-3:8800
      "

# -------------=== pbft engines ===-------------

  pbft-0:
    image: hyperledger/sawtooth-pbft-engine:nightly
    container_name: sawtooth-pbft-engine-default-0
    command: pbft-engine -vv --connect tcp://validator-0:5050
    stop_signal: SIGKILL

  pbft-1:
    image: hyperledger/sawtooth-pbft-engine:nightly
    container_name: sawtooth-pbft-engine-default-1
    command: pbft-engine -vv --connect tcp://validator-1:5050
    stop_signal: SIGKILL

  pbft-2:
    image: hyperledger/sawtooth-pbft-engine:nightly
    container_name: sawtooth-pbft-engine-default-2
    command: pbft-engine -vv --connect tcp://validator-2:5050
    stop_signal: SIGKILL

  pbft-3:
    image: hyperledger/sawtooth-pbft-engine:nightly
    container_name: sawtooth-pbft-engine-default-3
    command: pbft-engine -vv --connect tcp://validator-3:5050
    stop_signal: SIGKILL

  pbft-4:
    image: hyperledger/sawtooth-pbft-engine:nightly
    container_name: sawtooth-pbft-engine-default-4
    command: pbft-engine -vv --connect tcp://validator-4:5050
    stop_signal: SIGKILL

交易处理器的代码如下

import sys

from sawtooth_sdk.processor.core import TransactionProcessor
from sawtooth_sdk.processor.handler import TransactionHandler

from transaction_family import TransactionPayload, State, InvalidAction, \
    NAMESPACE, NAME, VERSION

import logging

logger = logging.getLogger(__name__)


class CustomTransactionHandler(TransactionHandler):

    @property
    def family_name(self):
        return NAME

    @property
    def family_versions(self):
        return [VERSION]

    @property
    def namespaces(self):
        return [NAMESPACE]

    # The argument transaction is an instance of the class Transaction that
    # is created from the protobuf definition. Also, context is an instance of
    # the class Context from the python SDK.
    def apply(self, transaction, context):
        logger.error("inside apply")
        header = transaction.header
        signer = header.signer_public_key

        print(transaction.payload)

        transaction = TransactionPayload.from_bytes(transaction.payload)
        state = State(context)

        if transaction.action == 'insert':
            state.insert(transaction.key, transaction.value)
            pass
        elif transaction.action == 'delete':
            state.delete(transaction.key)
            pass
        else:
            raise InvalidAction(transaction.action)


def main():
    processor = TransactionProcessor(url="tcp://localhost:4004")
    processor.add_handler(CustomTransactionHandler())
    processor.start()


if __name__ == '__main__':
    logging.basicConfig(filename='example.log',
                        level=logging.DEBUG)
    logger.setLevel(logging.INFO)
    logger.info("hello")
    main()

交易族模块如下:

import hashlib
import sys

import cbor2

NAME = 'custom'
NAMESPACE = hashlib.sha512(NAME.encode('utf-8')).hexdigest()[:6]
VERSION = '1.0'


def generate_address(key):
    return NAMESPACE + hashlib.sha512(str(key).encode('utf-8')).hexdigest()[
                       -64:]


class TransactionPayload:

    def __init__(self, payload):
        action, key, value = cbor2.loads(payload)
        self._action = action
        self._key = int(key)
        self._value = int(value)

    @property
    def action(self):
        return self._action

    @property
    def key(self):
        return self._key

    @property
    def value(self):
        return self._value

    @classmethod
    def from_bytes(cls, payload):
        return cls(payload)


class State:

    def __init__(self, context):
        self._context = context

    def insert(self, key, value):
        address = generate_address(key)
        n_req_bytes = (value.bit_length() + 7) // 8
        self._context.set_state({address: value.to_bytes(n_req_bytes,
                                                         sys.byteorder)})

    def delete(self, key):
        address = generate_address(key)
        self._context.delete_state([address])


class InvalidAction(Exception):
    def __init__(self, msg):
        self._msg = msg

    def __str__(self):
        return self._msg

最后,我试图通过其访问其余 api 的客户端如下:

import random
from hashlib import sha512
import requests
import cbor2
from sawtooth_sdk.protobuf.batch_pb2 import BatchHeader, Batch, BatchList
from sawtooth_sdk.protobuf.transaction_pb2 import TransactionHeader, Transaction
from sawtooth_signing import create_context
from sawtooth_signing import CryptoFactory
from transaction_family import generate_address, NAME, VERSION
import secrets

context = create_context('secp256k1')
private_key = context.new_random_private_key()
print(private_key)
signer = CryptoFactory(context).new_signer(private_key)


def insert(key, val):
    payload_bytes = cbor2.dumps(['insert', key, val])
    address = generate_address(key)

    txn_header_bytes = TransactionHeader(
        family_name=NAME,
        family_version=VERSION,
        inputs=[address],
        outputs=[address],
        signer_public_key=signer.get_public_key().as_hex(),
        batcher_public_key=signer.get_public_key().as_hex(),
        dependencies=[],
        payload_sha512=sha512(payload_bytes).hexdigest(),
        nonce=secrets.token_hex(16),
        # nonce=hex(random.randint(0, 2**64))
    ).SerializeToString()
    signature = signer.sign(txn_header_bytes)
    txn = Transaction(header=txn_header_bytes,
                      header_signature=signature,
                      payload=payload_bytes)

    txns = [txn]
    batch_header_bytes = BatchHeader(
        signer_public_key=signer.get_public_key().as_hex(),
        transaction_ids=[txn.header_signature for txn in txns]
    ).SerializeToString()
    signature = signer.sign(batch_header_bytes)
    batch = Batch(
        header=batch_header_bytes,
        header_signature=signature,
        transactions=txns,
        trace=True
    )

    batch_list_bytes = BatchList(batches=[batch]).SerializeToString()

    print(batch_list_bytes)

    # send request
    resp = requests.post(
        'http://localhost:8008/batches',
        headers={'Content-Type': 'application/octet-stream'},
        data=batch_list_bytes)

    print(resp)
    print(resp.json())


def main():
    insert(1, 2)


if __name__ == '__main__':
    main()

我看到您为 docker 图片使用夜间版本。

夜间版是开发者更新比较频繁的版本,不一定是稳定版。

相反,我建议您将 nightly 替换为 latest,这是一个更稳定的版本。我已经遇到了这个问题,这是我找到的让你的处理器注册验证器的唯一方法。

您可以从 docker 网站找到版本:https://hub.docker.com/u/hyperledger

我想通了。这是我的错误。我试图 运行 只有一个事务处理器实例。但是,每个节点都需要一个事务处理器实例。一旦我修改了我的 docker-compose 文件以包含所有五个节点的事务处理器,它就按预期工作了。

如果其他人遇到类似问题,请发布此答案。