交易提交到锯齿形intkey TP无响应

No response when transaction is submitted to sawtooth intkey TP

我正在尝试使用 hyperledger 锯齿设置交易处理器。我用锯齿波 1.0 测试了我的 TP,它工作正常。但是当我使用 sawtooth 1.1 网络时,我的交易没有被处理。看起来请求没有到达 TP。然后我尝试了 sdk 中的 intkey TP,它也有同样的问题。我匹配了文档中的交易提交过程,但没有用。

锯齿波网络docker文件

version: "2.1"

services:

  settings-tp:
    image: hyperledger/sawtooth-settings-tp:1.1
    container_name: sawtooth-settings-tp-default
    depends_on:
      - validator
    entrypoint: settings-tp -vv -C tcp://validator:4004

  validator:
    image: hyperledger/sawtooth-validator:1.1
    container_name: sawtooth-validator-default
    expose:
      - 4004
    ports:
      - "4004:4004"
    # start the validator with an empty genesis batch
    entrypoint: "bash -c \"\
        sawadm keygen && \
        sawtooth keygen my_key && \
        sawset genesis -k /root/.sawtooth/keys/my_key.priv && \
        sawadm genesis config-genesis.batch && \
        sawtooth-validator -vv \
          --endpoint tcp://validator:8800 \
          --bind component:tcp://eth0:4004 \
          --bind network:tcp://eth0:8800 \
        \""

  rest-api:
    image: hyperledger/sawtooth-rest-api:1.1
    container_name: sawtooth-rest-api-default
    ports:
      - "8008:8008"
    depends_on:
      - validator
    entrypoint: sawtooth-rest-api -C tcp://validator:4004 --bind rest-api:8008

交易处理器

/**
 * Copyright 2016 Intel Corporation
 *
 * 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.
 * ------------------------------------------------------------------------------
 */

'use strict'

const { TransactionHandler } = require('sawtooth-sdk/processor/handler')
const {
  InvalidTransaction,
  InternalError
} = require('sawtooth-sdk/processor/exceptions')

const crypto = require('crypto')
const cbor = require('cbor')

// Constants defined in intkey specification
const MIN_VALUE = 0
const MAX_VALUE = 4294967295
const MAX_NAME_LENGTH = 20

const _hash = (x) =>
  crypto.createHash('sha512').update(x).digest('hex').toLowerCase()

const INT_KEY_FAMILY = 'intkey'
const INT_KEY_NAMESPACE = _hash(INT_KEY_FAMILY).substring(0, 6)

const _decodeCbor = (buffer) =>
  new Promise((resolve, reject) =>
    cbor.decodeFirst(buffer, (err, obj) => (err ? reject(err) : resolve(obj)))
  )

const _toInternalError = (err) => {
  let message = (err.message) ? err.message : err
  throw new InternalError(message)
}

const _setEntry = (context, address, stateValue) => {
  let entries = {
    [address]: cbor.encode(stateValue)
  }
  return context.setState(entries)
}

const _applySet = (context, address, name, value) => (possibleAddressValues) => {
  let stateValueRep = possibleAddressValues[address]

  let stateValue
  if (stateValueRep && stateValueRep.length > 0) {
    stateValue = cbor.decodeFirstSync(stateValueRep)
    let stateName = stateValue[name]
    if (stateName) {
      throw new InvalidTransaction(
        `Verb is "set" but Name already in state, Name: ${name} Value: ${stateName}`
      )
    }
  }

  // 'set' passes checks so store it in the state
  if (!stateValue) {
    stateValue = {}
  }

  stateValue[name] = value

  return _setEntry(context, address, stateValue)
}

const _applyOperator = (verb, op) => (context, address, name, value) => (possibleAddressValues) => {
  let stateValueRep = possibleAddressValues[address]
  if (!stateValueRep || stateValueRep.length === 0) {
    throw new InvalidTransaction(`Verb is ${verb} but Name is not in state`)
  }

  let stateValue = cbor.decodeFirstSync(stateValueRep)
  if (stateValue[name] === null || stateValue[name] === undefined) {
    throw new InvalidTransaction(`Verb is ${verb} but Name is not in state`)
  }

  const result = op(stateValue[name], value)

  if (result < MIN_VALUE) {
    throw new InvalidTransaction(
      `Verb is ${verb}, but result would be less than ${MIN_VALUE}`
    )
  }

  if (result > MAX_VALUE) {
    throw new InvalidTransaction(
      `Verb is ${verb}, but result would be greater than ${MAX_VALUE}`
    )
  }

  // Increment the value in state by value
  // stateValue[name] = op(stateValue[name], value)
  stateValue[name] = result
  return _setEntry(context, address, stateValue)
}

const _applyInc = _applyOperator('inc', (x, y) => x + y)
const _applyDec = _applyOperator('dec', (x, y) => x - y)

class IntegerKeyHandler extends TransactionHandler {
  constructor () {
    super(INT_KEY_FAMILY, ['1.0'], [INT_KEY_NAMESPACE])
  }

  apply (transactionProcessRequest, context) {
    return _decodeCbor(transactionProcessRequest.payload)
      .catch(_toInternalError)
      .then((update) => {
        //
        // Validate the update
        let name = update.Name
        if (!name) {
          throw new InvalidTransaction('Name is required')
        }

        if (name.length > MAX_NAME_LENGTH) {
          throw new InvalidTransaction(
            `Name must be a string of no more than ${MAX_NAME_LENGTH} characters`
          )
        }

        let verb = update.Verb
        if (!verb) {
          throw new InvalidTransaction('Verb is required')
        }

        let value = update.Value
        if (value === null || value === undefined) {
          throw new InvalidTransaction('Value is required')
        }

        let parsed = parseInt(value)
        if (parsed !== value || parsed < MIN_VALUE || parsed > MAX_VALUE) {
          throw new InvalidTransaction(
            `Value must be an integer ` +
            `no less than ${MIN_VALUE} and ` +
            `no greater than ${MAX_VALUE}`)
        }

        value = parsed

        // Determine the action to apply based on the verb
        let actionFn
        if (verb === 'set') {
          actionFn = _applySet
        } else if (verb === 'dec') {
          actionFn = _applyDec
        } else if (verb === 'inc') {
          actionFn = _applyInc
        } else {
          throw new InvalidTransaction(`Verb must be set, inc, dec not ${verb}`)
        }

        let address = INT_KEY_NAMESPACE + _hash(name).slice(-64)

        // Get the current state, for the key's address:
        let getPromise = context.getState([address])

        // Apply the action to the promise's result:
        let actionPromise = getPromise.then(
          actionFn(context, address, name, value)
        )

        // Validate that the action promise results in the correctly set address:
        return actionPromise.then(addresses => {
          if (addresses.length === 0) {
            throw new InternalError('State Error!')
          }
          console.log(`Verb: ${verb} Name: ${name} Value: ${value}`)
        })
      })
  }
}

module.exports = IntegerKeyHandler

发送交易

const {createContext, CryptoFactory} = require('sawtooth-sdk/signing')
const cbor = require('cbor')
const {createHash} = require('crypto')
const {protobuf} = require('sawtooth-sdk')
const crypto = require('crypto')

// Creating a Private Key and Signer
const context = createContext('secp256k1')
const privateKey = context.newRandomPrivateKey()
const signer = new CryptoFactory(context).newSigner(privateKey)
const _hash = (x) => crypto.createHash('sha512').update(x).digest('hex').toLowerCase()


// Encoding Your Payload
const payload = {
    Verb: 'get',
    Name: 'foo',
    Value: null
}
const payloadBytes = cbor.encode(payload)

let familyAddr = _hash('intkey').substring(0, 6);
let nameAddr = _hash(payload.Name).slice(-64);
let addr = familyAddr + nameAddr;
console.log(addr);


// Create the Transaction Header
const transactionHeaderBytes = protobuf.TransactionHeader.encode({
    familyName: 'intkey',
    familyVersion: '1.0',
    inputs: [addr],
    outputs: [addr],
    signerPublicKey: signer.getPublicKey().asHex(),
    batcherPublicKey: signer.getPublicKey().asHex(),
    dependencies: [],
    payloadSha512: createHash('sha512').update(payloadBytes).digest('hex')
}).finish()

// Create the Transaction
const signature = signer.sign(transactionHeaderBytes)
const transaction = protobuf.Transaction.create({
    header: transactionHeaderBytes,
    headerSignature: signature,
    payload: payloadBytes
})

//  Create the BatchHeader
const transactions = [transaction]
const batchHeaderBytes = protobuf.BatchHeader.encode({
    signerPublicKey: signer.getPublicKey().asHex(),
    transactionIds: transactions.map((txn) => txn.headerSignature),
}).finish()

// Create the Batch
const headerSignature = signer.sign(batchHeaderBytes)
const batch = protobuf.Batch.create({
    header: batchHeaderBytes,
    headerSignature: headerSignature,
    transactions: transactions
})

// Encode the Batch(es) in a BatchList
const batchListBytes = protobuf.BatchList.encode({
    batches: [batch]
}).finish()

// Submitting Batches to the Validator
const request = require('request')
request.post({
    url: 'http://localhost:8008/batches',
    body: batchListBytes,
    headers: {'Content-Type': 'application/octet-stream'}
}, (err, response) => {
    if (err) return console.log(err)
    console.log(response.body)
})

从 1.0 迁移到 1.1 时,Hyperledger Sawtooth 中存在架构差异。一个主要区别是共识引擎被移到了验证器服务之外。在你的 docker-compose 文件中,没有共识引擎组件,验证器服务也没有监听共识引擎的端口。

共识引擎驱动区块创建。例如,PoET 中的定时器到期事件将导致验证者创建块、验证、广播给网络中的其他成员。此外,来自共识引擎的确认将使验证器服务将块提交到区块链。

请在此处找到示例 docker-使用 PoET 共识引擎撰写文件 https://github.com/hyperledger/sawtooth-core/blob/1-1/docker/compose/sawtooth-default-poet.yaml . Additionally you may try out https://github.com/hyperledger/sawtooth-core/blob/1-1/docker/compose/sawtooth-default.yaml 用于本地开发测试。

Hyperledger Sawtooth 常见问题解答中有关于从 1.0 版升级到 1.1 版的说明https://sawtooth.hyperledger.org/faq/upgrade/#id1。请随时提出更多问题或建议以更新这些文档。

不同版本也可以参考官方文档详细了解https://sawtooth.hyperledger.org/docs/