hyperledger-archives / sawtooth-sdk-java

https://wiki.hyperledger.org/display/sawtooth
Apache License 2.0
19 stars 38 forks source link

Getting an Exception from the Validator when trying to make a transaction! #35

Open marwyn-the-developer opened 3 years ago

marwyn-the-developer commented 3 years ago

I tried submitting a batch with a single transaction of the intkey transaction family to a standard development Sawtooth setup. But I get the following exception from the Validator:

[2021-04-02 17:27:43.273 ERROR    threadpool] (Signature) Unhandled exception during execution of task _HandlerManager.execute.<locals>.wrapped
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/sawtooth_signing/secp256k1.py", line 92, in from_hex
    return Secp256k1PublicKey.from_bytes(binascii.unhexlify(hex_str))
  File "/usr/lib/python3/dist-packages/sawtooth_signing/secp256k1.py", line 86, in from_bytes
    public_key = secp256k1.PublicKey(byte_str, raw=True, ctx=__CTX__)
  File "/usr/lib/python3/dist-packages/secp256k1/__init__.py", line 210, in __init__
    self.public_key = self.deserialize(pubkey)
  File "/usr/lib/python3/dist-packages/secp256k1/__init__.py", line 235, in deserialize
    raise Exception("unknown public key size (expected 33 or 65)")
Exception: unknown public key size (expected 33 or 65)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/sawtooth_validator/concurrent/threadpool.py", line 83, in wrapper
    return_value = fn(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/sawtooth_validator/networking/dispatch.py", line 444, in wrapped
    return callback(self._handler.handle(connection_id, message))
  File "/usr/lib/python3/dist-packages/sawtooth_validator/gossip/signature_verifier.py", line 248, in handle
    if not all(map(is_valid_batch, message_content.batches)):
  File "/usr/lib/python3/dist-packages/sawtooth_validator/gossip/signature_verifier.py", line 69, in is_valid_batch
    public_key = Secp256k1PublicKey.from_hex(header.signer_public_key)
  File "/usr/lib/python3/dist-packages/sawtooth_signing/secp256k1.py", line 94, in from_hex
    raise ParseError('Unable to parse hex public key: {}'.format(e))
sawtooth_signing.core.ParseError: Unable to parse hex public key: unknown public key size (expected 33 or 65)

I am using the Sawtooth Signing SDK for creating the key pair and for signing the transaction and the batch. I found out while debugging, that the public key consists of 66 hex digits while the validator expects 33 or 65.

Here is the relevant code;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.ByteString;
import com.storr.tp.framework.storrtpframework.transactionhandler.utils.PayloadMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;
import sawtooth.sdk.processor.Utils;
import sawtooth.sdk.protobuf.*;
import sawtooth.sdk.signing.Signer;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;

public class TransactionClientImpl implements TransactionClient {

    private final Signer signer;

    private final PayloadMapper payloadMapper;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final ObjectMapper mapper = new ObjectMapper();

    private final RestTemplate restTemplate;

    private final String baseUrl;

    public TransactionClientImpl(PayloadMapper payloadMapper, String baseUrl, RestTemplate restTemplate, Signer signer) {
        this.payloadMapper = payloadMapper;
        this.restTemplate = restTemplate;
        this.baseUrl = baseUrl;
        this.signer = signer;
    }

    public void sendTransaction(String transactionFamilyName, String transactionFamilyVersion, Collection<String> inputs, Collection<String> outputs, String action, Object payload) throws URISyntaxException {
        Transaction transaction = buildTransaction(transactionFamilyName, transactionFamilyVersion, inputs, outputs, action, payload);
        BatchList batchList = buildSingleBatch(transaction);
        URI uri = new URI(baseUrl + "/batches");
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/octet-stream");
        HttpEntity<byte[]> request = new HttpEntity<>(batchList.toByteArray(), headers);
        restTemplate.postForLocation(uri, request);
    }

    private BatchList buildSingleBatch(Transaction transaction) {
        BatchHeader batchHeader = BatchHeader.newBuilder()
                .addTransactionIds(transaction.getHeaderSignature())
                .build();
        String batchSignature = signer.sign(batchHeader.toByteArray());
        Batch batch = Batch.newBuilder()
                .setHeader(batchHeader.toByteString())
                .addTransactions(transaction)
                .setHeaderSignature(batchSignature)
                .build();
        return BatchList.newBuilder()
                .addBatches(batch)
                .build();
    }

    private Transaction buildTransaction(String transactionFamilyName, String transactionFamilyVersion, Collection<String> inputs, Collection<String> outputs, String action, Object payload) {
        Transaction transaction = null;
        try {
            Map<String, Object> payloadAsMap = mapper.convertValue(payload, new TypeReference<>() {
            });
            ByteString payloadBytes = payloadMapper.marshal(payloadAsMap);
            String payloadHash = Utils.hash512(payloadBytes.toByteArray());
            TransactionHeader transactionHeader = TransactionHeader.newBuilder()
                    .setSignerPublicKey(signer.getPublicKey().hex())
                    .setFamilyName(transactionFamilyName)
                    .setFamilyVersion(transactionFamilyVersion)
                    .addAllInputs(inputs)
                    .addAllOutputs(outputs)
                    .setPayloadSha512(payloadHash)
                    .setBatcherPublicKey(signer.getPublicKey().hex())
                    .setNonce(UUID.randomUUID().toString())
                    .build();
            String signature = signer.sign(transactionHeader.toByteArray());
            transaction = Transaction.newBuilder()
                    .setHeader(transactionHeader.toByteString())
                    .setPayload(payloadBytes)
                    .setHeaderSignature(signature)
                    .build();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

        return transaction;
    }
}

and the ioc configuration (I am using spring)

@Bean
    public PayloadMapper payloadMapper() {
        return new PayloadMapper() {
            final com.fasterxml.jackson.databind.ObjectMapper objectMapper = new ObjectMapper(new CBORFactory());

            @Override
            public ByteString marshal(Object object) throws Exception {
                return ByteString.copyFrom(objectMapper.writeValueAsBytes(object));
            }

            @Override
            public LinkedHashMap<String, Object> unmarshal(ByteString data) throws Exception {
                return objectMapper.readValue(data.toByteArray(), new TypeReference<>() {
                });
            }
        };
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public Context context(@Value("secp256k1") String algorithm) {
        return CryptoFactory.createContext(algorithm);
    }

    @Bean
    public Signer signer(@Value("secp256k1") String algorithm) {
        Context context = context(algorithm);
        return new Signer(context, context.newRandomPrivateKey());
    }

    @Bean
    public TransactionClient transactionClient(@Value("http://127.0.0.1:8008") String baseUrl, @Value("secp256k1") String algorithm) {
        return new TransactionClientImpl(payloadMapper(), baseUrl, restTemplate(), signer(algorithm));
    }

I think this might be a bug from the Sawtooth Signing SDK.

marwyn-the-developer commented 3 years ago

I tried to submit transactions with the Sawtooth NodeJs SDK and the result was the same. Then I tried the Python SDK and it somehow seemed to work (The exception was not thrown). This seems to be a major bug and I think that immediate action is required in solving this issue.

w3villa-ankit commented 2 years ago

getting same issue

[2022-10-25 08:04:42.339 ERROR threadpool] (Client) Unhandled exception during execution of task _HandlerManager.execute..wrapped sawtooth-validator-default-0 | Traceback (most recent call last): sawtooth-validator-default-0 | File "/usr/lib/python3/dist-packages/sawtooth_validator/concurrent/threadpool.py", line 83, in wrapper sawtooth-validator-default-0 | return_value = fn(*args, **kwargs) sawtooth-validator-default-0 | File "/usr/lib/python3/dist-packages/sawtooth_validator/networking/dispatch.py", line 444, in wrapped sawtooth-validator-default-0 | return callback(self._handler.handle(connection_id, message)) sawtooth-validator-default-0 | File "/usr/lib/python3/dist-packages/sawtooth_validator/journal/completer.py", line 406, in handle sawtooth-validator-default-0 | self._completer.add_batch(batch) sawtooth-validator-default-0 | File "/usr/lib/python3/dist-packages/sawtooth_validator/journal/completer.py", line 343, in add_batch sawtooth-validator-default-0 | self._on_batch_received(batch) sawtooth-validator-default-0 | TypeError: 'NoneType' object is not callable