TP не получает транзакции после отклонения блока из-за несоответствия состояния корневого хеша Hyperledger Sawtooth - PullRequest
0 голосов
/ 05 июля 2018

Я установил Hyperlder Sawtooth Network из Sawtooth Docs, вы можете найти docker-compose.yaml Я использовал для настройки сети здесь:

https://sawtooth.hyperledger.org/docs/core/releases/1.0/app_developers_guide/sawtooth-default.yaml

Код процессора транзакций:

const { TransactionHandler } = require('sawtooth-sdk/processor/handler');
const { InvalidTransaction } = require('sawtooth-sdk/processor/exceptions');
const { TextEncoder, TextDecoder } = require('text-encoding/lib/encoding');
const crypto = require('crypto');

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

const encoder = new TextEncoder('utf8');
const decoder = new TextDecoder('utf8');

const TP_FAMILY = 'grocery';
const TP_NAMESPACE = _hash(TP_FAMILY).substring(0, 6);

class GroceryHandler extends TransactionHandler {
    constructor() {
        super(TP_FAMILY, ['1.0.0'], [TP_NAMESPACE]);
        this.timeout = 500;
    }

    apply(request, context) {
        console.log('Transaction Processor Called!');
        this._context = context;
        this._request = request;

        const actions = ['createOrder'];

        try {
            let payload = JSON.parse(decoder.decode(request.payload));
            let action = payload.action

            if(!action || !actions.includes(action)) {
                throw new InvalidTransaction(`Upsupported action "${action}"!`);
            }

            try {
                return this[action](payload.data);
            } catch(e) {
                console.log(e);
            }
        } catch(e) {
            throw new InvalidTransaction('Pass a valid json string.');
        }
    }

    createOrder(payload) {
        console.log('Creating order!');
        let data = {
            id: payload.id,
            status: payload.status,
            created_at: Math.floor((new Date()).getTime() / 1000)
        };

        return this._setEntry(this._makeAddress(payload.id), data);
    }    

    _setEntry(address, payload) {
        let dataBytes = encoder.encode(JSON.stringify(payload));
        let entries = {
            [address]: dataBytes
        }
        return this._context.setState(entries);
    }

    _makeAddress(id) {
        return TP_NAMESPACE + _hash(id).substr(0,64);
    }
}

const transactionProcessor = new TransactionProcessor('tcp://validator:4004');
transactionProcessor.addHandler(new GroceryHandler());
transactionProcessor.start();

Код клиента:

const { createContext, CryptoFactory } = require('sawtooth-sdk/signing');
const { protobuf } = require('sawtooth-sdk');
const { TextEncoder } = require('text-encoding/lib/encoding');
const request = require('request');
const crypto = require('crypto');

const encoder = new TextEncoder('utf8');

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

const TP_FAMILY = 'grocery';
const TP_NAMESPACE = _hash(TP_FAMILY).substr(0, 6);

const context = createContext('secp256k1');
const privateKey = context.newRandomPrivateKey();
const signer = new CryptoFactory(context).newSigner(privateKey);

let payload = {
    action: 'create_order',
    data: {
        id: '1'
    }
};

const address = TP_NAMESPACE + _hash(payload.id).substr(0, 64);
const payloadBytes = encoder.encode(JSON.stringify(payload));

const transactionHeaderBytes = protobuf.TransactionHeader.encode({
    familyName: TP_FAMILY,
    familyVersion: '1.0.0',
    inputs: [address],
    outputs: [address],
    signerPublicKey: signer.getPublicKey().asHex(),
    batcherPublicKey: signer.getPublicKey().asHex(),
    dependencies: [],
    payloadSha512: _hash(payloadBytes)
}).finish();

const transactionHeaderSignature = signer.sign(transactionHeaderBytes);

const transaction = protobuf.Transaction.create({
    header: transactionHeaderBytes,
    headerSignature: transactionHeaderSignature,
    payload: payloadBytes
});

const transactions = [transaction]

const batchHeaderBytes = protobuf.BatchHeader.encode({
    signerPublicKey: signer.getPublicKey().asHex(),
    transactionIds: transactions.map((txn) => txn.headerSignature),
}).finish();

const batchHeaderSignature = signer.sign(batchHeaderBytes)

const batch = protobuf.Batch.create({
    header: batchHeaderBytes,
    headerSignature: batchHeaderSignature,
    transactions: transactions
});

const batchListBytes = protobuf.BatchList.encode({
    batches: [batch]
}).finish();


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);
});

Журнал валидатора: https://justpaste.it/74y5g

Журнал процессора транзакций: https://justpaste.it/5ayn6

> grocery-tp@1.0.0 start /processor
> node index.js tcp://validator:4004

Connected to tcp://validator:4004
Registration of [grocery 1.0.0] succeeded

Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!
Transaction Processor Called!
Creating order!

После записи ниже в журналах валидатора я не получаю никаких транзакций в процессор.

[2018-07-04 10:39:18.026 DEBUG    block_validator] Block(c9636780f4babea6b8103665bc1fb19a59ce0ba66289494fc61972e97423a3273dd1d41e93ddf90c933809dab5350a0a83b282aaf25ebdcc6619735e25d8b337 (block_num:75, state:00704f66a517e79dc064e63586b12d677a3b60ce25363a4654fa819a59e4132c, previous_block_id:32b07cd79093aee0b7833b8924c8fef01fce798f3d58560c83c9891b2c05c02f2a4b894de43503fdcb0f129e9f365cfbdc415b798877393f7e75598195ad3c94)) rejected due to state root hash mismatch: 00704f66a517e79dc064e63586b12d677a3b60ce25363a4654fa819a59e4132c != e52737049078b9e0f149bb58fc4938473a5e889fa427536b0e862c4728df5004

1 Ответ

0 голосов
/ 11 июля 2018

Когда sawtooth обрабатывает транзакцию, она отправляет ее на ваш TP более одного раза , а затем сравнивает хэш между несколькими вызовами, чтобы убедиться, что возвращается один и тот же результат. Если в TP вы создаете другой адрес или вариацию данных, хранящихся по адресу, транзакция будет неудачной.

Известная поговорка в пилообразной форме гласит, что TP должен быть детерминированным для каждой транзакции, другими словами, это похоже на правило в программировании функций: один и тот же TP, вызываемый с той же транзакцией, должен давать тот же результат.

На что обратить внимание:

  1. Будьте осторожны, чтобы не создавать адрес, который включает элементы меток времени, счетчик приращений или другие случайные биты информации
  2. Будьте осторожны, чтобы не делать то же самое для данных, которые вы храните по адресу
...