Обработчик пользовательских транзакций не получает запрос - PullRequest
0 голосов
/ 11 мая 2018

Почему мой процессор транзакций не получает запрос, который я отправляю через API остальных?

Я построил клиент и процессор транзакций (TP) на Голанге, которые мало чем отличаются от примера XO. Я успешно установил TP на локальные компоненты Sawtooth и отправил списки партий из отдельного инструмента cli. В настоящее время метод apply в TP не используется и не получает ни одной из моих транзакций.

РЕДАКТИРОВАТЬ: Чтобы максимально упростить и прояснить мою проблему, я отказался от своего исходного исходного кода и построил более простой клиент, который отправляет транзакцию для примера XO SDK. *

Когда я запускаю созданный мной инструмент, остальные API успешно получают запрос, обрабатывают и возвращают ответ 202, но, как представляется, пропускают идентификатор пакета из URL-адресов статусов пакета. При проверке журналов создается впечатление, что валидатор никогда не получает запрос от остальных API, как показано в журналах ниже.

sawtooth-rest-api-default | [2018-05-16 09:16:38.861 DEBUG    route_handlers] Sending CLIENT_BATCH_SUBMIT_REQUEST request to validator
sawtooth-rest-api-default | [2018-05-16 09:16:38.863 DEBUG    route_handlers] Received CLIENT_BATCH_SUBMIT_RESPONSE response from validator with status OK
sawtooth-rest-api-default | [2018-05-16 09:16:38.863 INFO     helpers] POST /batches HTTP/1.1: 202 status, 213 size, in 0.002275 s

Ниже приведен мой инструмент командной строки, который отправляет транзакции в локальный экземпляр.

package main

import (
    "bytes"
    "crypto/sha512"
    "encoding/base64"
    "encoding/hex"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "math/rand"
    "net/http"
    "strings"
    "time"

    "github.com/hyperledger/sawtooth-sdk-go/protobuf/batch_pb2"
    "github.com/hyperledger/sawtooth-sdk-go/protobuf/transaction_pb2"
    "github.com/hyperledger/sawtooth-sdk-go/signing"
)

var restAPI string

func main() {
    var hostname, port string

    flag.StringVar(&hostname, "hostname", "localhost", "The hostname to host the application on (default: localhost).")
    flag.StringVar(&port, "port", "8080", "The port to listen on for connection (default: 8080)")
    flag.StringVar(&restAPI, "restAPI", "http://localhost:8008", "The address of the sawtooth REST API")

    flag.Parse()

    s := time.Now()
    ctx := signing.CreateContext("secp256k1")
    key := ctx.NewRandomPrivateKey()
    snr := signing.NewCryptoFactory(ctx).NewSigner(key)

    payload := "testing_new,create,"
    encoded := base64.StdEncoding.EncodeToString([]byte(payload))

    trn := BuildTransaction(
        "testing_new",
        encoded,
        "xo",
        "1.0",
        snr)

    trn.Payload = []byte(encoded)

    batchList := &batch_pb2.BatchList{
        Batches: []*batch_pb2.Batch{
            BuildBatch(
                []*transaction_pb2.Transaction{trn},
                snr),
        },
    }

    serialised := batchList.String()

    fmt.Println(serialised)

    resp, err := http.Post(
        restAPI+"/batches",
        "application/octet-stream",
        bytes.NewReader([]byte(serialised)),
    )

    if err != nil {
        fmt.Println("Error")
        fmt.Println(err.Error())
        return
    }

    defer resp.Body.Close()
    fmt.Println(resp.Status)
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
    elapsed := time.Since(s)
    log.Printf("Creation took %s", elapsed)

    resp.Close = true
}

// BuildTransaction will build a transaction based on the information provided
func BuildTransaction(ID, payload, familyName, familyVersion string, snr *signing.Signer) *transaction_pb2.Transaction {
    publicKeyHex := snr.GetPublicKey().AsHex()
    payloadHash := Hexdigest(string(payload))

    addr := Hexdigest(familyName)[:6] + Hexdigest(ID)[:64]

    transactionHeader := &transaction_pb2.TransactionHeader{
        FamilyName:       familyName,
        FamilyVersion:    familyVersion,
        SignerPublicKey:  publicKeyHex,
        BatcherPublicKey: publicKeyHex,
        Inputs:           []string{addr},
        Outputs:          []string{addr},
        Dependencies:     []string{},
        PayloadSha512:    payloadHash,
        Nonce:            GenerateNonce(),
    }

    header := transactionHeader.String()
    headerBytes := []byte(header)
    headerSig := hex.EncodeToString(snr.Sign(headerBytes))

    return &transaction_pb2.Transaction{
        Header:          headerBytes,
        HeaderSignature: headerSig[:64],
        Payload:         []byte(payload),
    }
}

// BuildBatch will build a batch using the provided transactions
func BuildBatch(trans []*transaction_pb2.Transaction, snr *signing.Signer) *batch_pb2.Batch {

    ids := []string{}

    for _, t := range trans {
        ids = append(ids, t.HeaderSignature)
    }

    batchHeader := &batch_pb2.BatchHeader{
        SignerPublicKey: snr.GetPublicKey().AsHex(),
        TransactionIds:  ids,
    }

    return &batch_pb2.Batch{
        Header:          []byte(batchHeader.String()),
        HeaderSignature: hex.EncodeToString(snr.Sign([]byte(batchHeader.String())))[:64],
        Transactions:    trans,
    }
}

// Hexdigest will hash the string and return the result as hex
func Hexdigest(str string) string {
    hash := sha512.New()
    hash.Write([]byte(str))
    hashBytes := hash.Sum(nil)
    return strings.ToLower(hex.EncodeToString(hashBytes))
}

// GenerateNonce will generate a random string to use
func GenerateNonce() string {
    return randStringBytesMaskImprSrc(16)
}

const (
    letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

func randStringBytesMaskImprSrc(n int) string {
    rand.Seed(time.Now().UnixNano())
    b := make([]byte, n)
    // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
    for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = rand.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return string(b)
}

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

[Редактировать]

Удалось обновить документ с информацией о написании клиента на Go https://sawtooth.hyperledger.org/docs/core/nightly/master/app_developers_guide/go_sdk.html

[Оригинальный ответ]

Отвечая на вопрос немного позже, надеюсь, это поможет другим, кто сталкивается с похожими проблемами с клиентом Go.

Недавно я пробовал пример клиента Go для Sawtooth.Столкнулся с похожими проблемами, которые вы задали здесь, в настоящее время трудно отладить то, что пошло не так в составленном списке пакетов.Проблема заключается в отсутствии примера кода и документации для использования Go SDK при разработке клиентских приложений.

Вот ссылка на пример работающего кода Go: https://github.com/arsulegai/contentprotection/tree/master/ContentProtectionGoClient

Пожалуйста, посмотрите файлsrc / client / client.go, который составляет одну транзакцию и пакет, помещает его в список пакетов и отправляет его в валидатор.Метод, который я использовал для отладки, состоит в том, чтобы составить список ожидаемых пакетов (для конкретной транзакции) на другом языке и сравнить каждый шаг с результатом эквивалентного шага в коде Go.

Переходя к вопросу, вместе с отсутствующей информацией вЗаголовок составленной транзакции Другой проблемой может быть способ сериализации сообщений Protobuf.Пожалуйста, используйте библиотеку protobuf для сериализации.

Пример: (расширение ответа от @danielcooperxyz)

transactionHeader := &transaction_pb2.TransactionHeader{
    FamilyName:       familyName,
    FamilyVersion:    familyVersion,
    SignerPublicKey:  publicKeyHex,
    BatcherPublicKey: publicKeyHex,
    Inputs:           []string{addr},
    Outputs:          []string{addr},
    Dependencies:     []string{},
    PayloadSha512:    payloadHash,
    Nonce:            uuid.NewV4(),
}
transactionHeaderSerializedForm, _ := proto.Marshal(transactionHeader)

(методы библиотеки protobuf можно найти в github.com/golang/protobuf/proto)

0 голосов
/ 31 мая 2018

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

Полнота транзакции

Как@Frank C. В комментариях над заголовками моей транзакции отсутствовала пара значений.Это были адреса, а также одноразовый номер.

// Hexdigest will hash the string and return the result as hex
func Hexdigest(str string) string {
    hash := sha512.New()
    hash.Write([]byte(str))
    hashBytes := hash.Sum(nil)
    return strings.ToLower(hex.EncodeToString(hashBytes))
}


addr := Hexdigest(familyName)[:6] + Hexdigest(ID)[:64]
transactionHeader := &transaction_pb2.TransactionHeader{
    FamilyName:       familyName,
    FamilyVersion:    familyVersion,
    SignerPublicKey:  publicKeyHex,
    BatcherPublicKey: publicKeyHex,
    Inputs:           []string{addr},
    Outputs:          []string{addr},
    Dependencies:     []string{},
    PayloadSha512:    payloadHash,
    Nonce:            uuid.NewV4(),
} 

Трассировка

Следующим шагом было включение трассировки в пакете.

return &batch_pb2.Batch{
    Header:          []byte(batchHeader.String()),
    HeaderSignature: batchHeaderSignature,
    Transactions:    trans,
    Trace: true, // Set this flag to true
}

При указанном выше значении остальныеAPI будет декодировать сообщение для печати дополнительной информации о регистрации, а также компонент Validator выведет более полезную запись в журнал.

400 Bad Request
{
"error": {
"code": 35,
"message": "The protobuf BatchList you submitted was malformed and could not be read.",
"title": "Protobuf Not Decodable"
}
}

Выше было выведено API отдыха после включения трассировки.Это доказало, что с полученными данными было что-то не так.

Почему это так?
Следуя некоторым ценным советам из чатов Sawtooth, я пытался десериализировать свои партии, используяSDK для другого языка.

Десериализация

Чтобы протестировать десериализацию пакетов в другом SDK, я создал веб-API в python, на который я мог бы легко отправлять свои пакеты, чтобы попытаться десериализовать их.

from flask import Flask, request

from protobuf import batch_pb2

app = Flask(__name__)

@app.route("/batches", methods = [ 'POST' ])
def deserialise():
    received = request.data
    print(received)
    print("\n")
    print(''.join('{:02x}'.format(x) for x in received))

    batchlist = batch_pb2.BatchList()

    batchlist.ParseFromString(received)
    return ""

if __name__ == '__main__':
    app.run(host="0.0.0.0", debug=True)

После отправки моей партии я получил следующую ошибку.

RuntimeWarning: Unexpected end-group tag: Not all data was converted

Это было очевидно, что пошло не так с моими партиями, но, видя, что все это обрабатывалосьс помощью Hyperledger Sawtooth Go SDK я решил перейти на Python и создал свое приложение с этим.

...