由于状态根哈希不匹配,TP被拒绝后,TP无法接收事务Hyperledger Sawtooth

时间:2018-07-05 06:59:06

标签: blockchain hyperledger hyperledger-sawtooth

我已经从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 :(得分:3)

当锯齿处理事务时,它将多次将其发送到TP ,然后比较多个调用之间的哈希值以确保返回相同的结果。如果在TP内生成另一个地址或存储在该地址的数据变体,则交易将失败。

锯齿中的一句名言是,TP必须对于每个事务都是确定性的,换句话说,它与函数编程中的规则相似:使用相同Transaction调用的相同TP应该产生相同的结果。

注意事项:

  1. 请注意不要构造包含时间戳记元素,增量计数或其他随机信息位的地址
  2. 请注意不要对存储在地址中的数据执行相同的操作