代码之家  ›  专栏  ›  技术社区  ›  Rohit Khatri

由于状态根哈希不匹配,在块被拒绝后,tp未接收事务hyperledger sawtooth

  •  1
  • Rohit Khatri  · 技术社区  · 6 年前

    我设置了一个 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 回复  |  直到 6 年前
        1
  •  3
  •   Frank C.    6 年前

    当sawtooth处理一个事务时,它会将它发送到您的tp 不止一次 然后比较多个调用之间的哈希,以确保返回相同的结果。如果在tp中,您正在生成一个不同的地址或存储在某个地址上的数据的变体,则事务将失败。

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

    注意事项:

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