OSDN Git Service

Sign process (#371)
authorPaladz <yzhu101@uottawa.ca>
Fri, 9 Feb 2018 05:50:23 +0000 (13:50 +0800)
committerGitHub <noreply@github.com>
Fri, 9 Feb 2018 05:50:23 +0000 (13:50 +0800)
* support sign between different node

* fix for golint

* sign password doesn't need array any more

blockchain/hsm.go
blockchain/hsm_test.go
blockchain/transact.go
blockchain/transact_test.go
blockchain/txbuilder/rawtxsig_witness.go
blockchain/txbuilder/signature_witness.go
blockchain/txbuilder/txbuilder.go
blockchain/txbuilder/txbuilder_test.go
blockchain/txbuilder/witness.go
test/integration/standard_transaction_test.go
test/util.go

index 19260cb..b7509f0 100644 (file)
@@ -50,8 +50,13 @@ func (bcr *BlockchainReactor) pseudohsmDeleteKey(ctx context.Context, x struct {
        return NewSuccessResponse(nil)
 }
 
+type signResp struct {
+       Tx           *txbuilder.Template `json:"transaction"`
+       SignComplete bool                `json:"sign_complete"`
+}
+
 func (bcr *BlockchainReactor) pseudohsmSignTemplates(ctx context.Context, x struct {
-       Password []string           `json:"password"`
+       Password string             `json:"password"`
        Txs      txbuilder.Template `json:"transaction"`
 }) Response {
        if err := txbuilder.Sign(ctx, &x.Txs, nil, x.Password, bcr.pseudohsmSignTemplate); err != nil {
@@ -59,7 +64,7 @@ func (bcr *BlockchainReactor) pseudohsmSignTemplates(ctx context.Context, x stru
                return NewErrorResponse(err)
        }
        log.Info("Sign Transaction complete.")
-       return NewSuccessResponse(&x.Txs)
+       return NewSuccessResponse(&signResp{Tx: &x.Txs, SignComplete: txbuilder.SignProgress(&x.Txs)})
 }
 
 func (bcr *BlockchainReactor) pseudohsmSignTemplate(ctx context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
index 2449ab9..c3e5a68 100755 (executable)
@@ -91,7 +91,7 @@ func TestHSM(t *testing.T) {
        }
        //go accounts.ProcessBlocks(ctx)
 
-       err = txbuilder.Sign(ctx, tmpl, nil, []string{"password"}, func(_ context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
+       err = txbuilder.Sign(ctx, tmpl, nil, "password", func(_ context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
                sigBytes, err := hsm.XSign(xpub, path, data[:], password)
                if err != nil {
                        return nil, nil
index 696de0d..4f12776 100644 (file)
@@ -43,7 +43,7 @@ func (bcr *BlockchainReactor) actionDecoder(action string) (func([]byte) (txbuil
        return decoder, true
 }
 
-func MergeActions(req *BuildRequest) []map[string]interface{} {
+func mergeActions(req *BuildRequest) []map[string]interface{} {
        actions := make([]map[string]interface{}, 0)
        actionMap := make(map[string]map[string]interface{})
 
@@ -75,7 +75,7 @@ func (bcr *BlockchainReactor) buildSingle(ctx context.Context, req *BuildRequest
        if err != nil {
                return nil, err
        }
-       reqActions := MergeActions(req)
+       reqActions := mergeActions(req)
        actions := make([]txbuilder.Action, 0, len(reqActions))
        for i, act := range reqActions {
                typ, ok := act["type"].(string)
@@ -144,8 +144,10 @@ func (bcr *BlockchainReactor) submitSingle(ctx context.Context, tpl *txbuilder.T
                return nil, errors.Wrap(txbuilder.ErrMissingRawTx)
        }
 
-       err := txbuilder.FinalizeTx(ctx, bcr.chain, tpl.Transaction)
-       if err != nil {
+       if err := txbuilder.MaterializeWitnesses(tpl); err != nil {
+               return nil, err
+       }
+       if err := txbuilder.FinalizeTx(ctx, bcr.chain, tpl.Transaction); err != nil {
                return nil, errors.Wrapf(err, "tx %s", tpl.Transaction.ID.String())
        }
 
@@ -221,15 +223,14 @@ func (bcr *BlockchainReactor) waitForTxInBlock(ctx context.Context, tx *legacy.T
 
 // POST /submit-transaction
 func (bcr *BlockchainReactor) submit(ctx context.Context, tpl *txbuilder.Template) Response {
-
-       txid, err := bcr.submitSingle(nil, tpl)
+       txID, err := bcr.submitSingle(nil, tpl)
        if err != nil {
                log.WithField("err", err).Error("submit single tx")
                return NewErrorResponse(err)
        }
 
-       log.WithField("txid", txid).Info("submit single tx")
-       return NewSuccessResponse(txid)
+       log.WithField("txid", txID["txid"]).Info("submit single tx")
+       return NewSuccessResponse(txID)
 }
 
 // POST /sign-submit-transaction
@@ -237,13 +238,10 @@ func (bcr *BlockchainReactor) signSubmit(ctx context.Context, x struct {
        Password []string           `json:"password"`
        Txs      txbuilder.Template `json:"transaction"`
 }) Response {
-
-       var err error
-       if err = txbuilder.Sign(ctx, &x.Txs, nil, x.Password, bcr.pseudohsmSignTemplate); err != nil {
+       if err := txbuilder.Sign(ctx, &x.Txs, nil, x.Password[0], bcr.pseudohsmSignTemplate); err != nil {
                log.WithField("build err", err).Error("fail on sign transaction.")
                return NewErrorResponse(err)
        }
-
        log.Info("Sign Transaction complete.")
 
        txID, err := bcr.submitSingle(nil, &x.Txs)
index bfc745b..dfd6bd6 100644 (file)
@@ -69,7 +69,7 @@ func TestMergeActions(t *testing.T) {
                        m["amount"] = json.Number(fmt.Sprintf("%v", amount))
                }
 
-               actions := MergeActions(BuildReq)
+               actions := mergeActions(BuildReq)
 
                if len(actions) != c.actionCount {
                        t.Fatalf("got error count %d, want %d", len(actions), c.actionCount)
index a616a29..0ea74f7 100755 (executable)
@@ -4,9 +4,10 @@ import (
        "context"
        "encoding/json"
 
+       log "github.com/sirupsen/logrus"
+
        "github.com/bytom/crypto/ed25519/chainkd"
        chainjson "github.com/bytom/encoding/json"
-       "github.com/bytom/errors"
 )
 
 // TODO(bobg): most of the code here is duplicated from
@@ -20,7 +21,7 @@ type RawTxSigWitness struct {
        Sigs   []chainjson.HexBytes `json:"signatures"`
 }
 
-func (sw *RawTxSigWitness) sign(ctx context.Context, tpl *Template, index uint32, xpubs []chainkd.XPub, auth []string, signFn SignFunc) error {
+func (sw *RawTxSigWitness) sign(ctx context.Context, tpl *Template, index uint32, xpubs []chainkd.XPub, auth string, signFn SignFunc) error {
        if len(sw.Sigs) < len(sw.Keys) {
                // Each key in sw.Keys may produce a signature in sw.Sigs. Make
                // sure there are enough slots in sw.Sigs and that we preserve any
@@ -48,9 +49,10 @@ func (sw *RawTxSigWitness) sign(ctx context.Context, tpl *Template, index uint32
                for i, p := range keyID.DerivationPath {
                        path[i] = p
                }
-               sigBytes, err := signFn(ctx, keyID.XPub, path, tpl.Hash(index).Byte32(), auth[i])
+               sigBytes, err := signFn(ctx, keyID.XPub, path, tpl.Hash(index).Byte32(), auth)
                if err != nil {
-                       return errors.WithDetailf(err, "computing signature %d", i)
+                       log.WithField("err", err).Warningf("computing signature %d", i)
+                       return nil
                }
                sw.Sigs[i] = sigBytes
        }
@@ -68,6 +70,7 @@ func (sw RawTxSigWitness) materialize(args *[][]byte) error {
        return nil
 }
 
+// MarshalJSON convert struct to json
 func (sw RawTxSigWitness) MarshalJSON() ([]byte, error) {
        obj := struct {
                Type   string               `json:"type"`
index 6b5e073..e0a3fa6 100755 (executable)
@@ -4,6 +4,8 @@ import (
        "context"
        "encoding/json"
 
+       log "github.com/sirupsen/logrus"
+
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/crypto/sha3pool"
        chainjson "github.com/bytom/encoding/json"
@@ -12,6 +14,7 @@ import (
 )
 
 type (
+       // SignatureWitness is a sign struct
        SignatureWitness struct {
                // Quorum is the number of signatures required.
                Quorum int `json:"quorum"`
@@ -35,6 +38,7 @@ type (
        }
 )
 
+// ErrEmptyProgram is a type of error
 var ErrEmptyProgram = errors.New("empty signature program")
 
 // Sign populates sw.Sigs with as many signatures of the predicate in
@@ -47,7 +51,7 @@ var ErrEmptyProgram = errors.New("empty signature program")
 //  - the mintime and maxtime of the transaction (if non-zero)
 //  - the outputID and (if non-empty) reference data of the current input
 //  - the assetID, amount, control program, and (if non-empty) reference data of each output.
-func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint32, xpubs []chainkd.XPub, auth []string, signFn SignFunc) error {
+func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint32, xpubs []chainkd.XPub, auth string, signFn SignFunc) error {
        // Compute the predicate to sign. This is either a
        // txsighash program if tpl.AllowAdditional is false (i.e., the tx is complete
        // and no further changes are allowed) or a program enforcing
@@ -91,9 +95,10 @@ func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint3
                for i, p := range keyID.DerivationPath {
                        path[i] = p
                }
-               sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth[i])
+               sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth)
                if err != nil {
-                       return errors.WithDetailf(err, "computing signature %d", i)
+                       log.WithField("err", err).Warningf("computing signature %d", i)
+                       return nil
                }
                sw.Sigs[i] = sigBytes
        }
@@ -118,6 +123,7 @@ func (sw SignatureWitness) materialize(args *[][]byte) error {
        return nil
 }
 
+// MarshalJSON convert struct to json
 func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
        obj := struct {
                Type   string               `json:"type"`
index a4b6e7c..c8c6081 100755 (executable)
@@ -15,6 +15,7 @@ import (
        "github.com/bytom/protocol/bc/legacy"
 )
 
+// errors
 var (
        ErrBadRefData          = errors.New("transaction reference data does not match previous template's reference data")
        ErrBadTxInputIdx       = errors.New("unsigned tx missing input")
@@ -70,7 +71,8 @@ func Build(ctx context.Context, tx *legacy.TxData, actions []Action, maxTime tim
        return tpl, nil
 }
 
-func Sign(ctx context.Context, tpl *Template, xpubs []chainkd.XPub, auth []string, signFn SignFunc) error {
+// Sign will try to sign all the witness
+func Sign(ctx context.Context, tpl *Template, xpubs []chainkd.XPub, auth string, signFn SignFunc) error {
        for i, sigInst := range tpl.SigningInstructions {
                for j, wc := range sigInst.WitnessComponents {
                        switch sw := wc.(type) {
@@ -87,7 +89,7 @@ func Sign(ctx context.Context, tpl *Template, xpubs []chainkd.XPub, auth []strin
                        }
                }
        }
-       return materializeWitnesses(tpl)
+       return nil
 }
 
 func checkBlankCheck(tx *legacy.TxData) error {
index 1731a5c..c2ea088 100755 (executable)
@@ -169,7 +169,7 @@ func TestSignatureWitnessMaterialize(t *testing.T) {
                        },
                },
        }}
-       err = materializeWitnesses(tpl)
+       err = MaterializeWitnesses(tpl)
        if err != nil {
                testutil.FatalErr(t, err)
        }
@@ -181,7 +181,7 @@ func TestSignatureWitnessMaterialize(t *testing.T) {
        // Test with exact amount of signatures required, in correct order
        component := tpl.SigningInstructions[0].WitnessComponents[0].(*SignatureWitness)
        component.Sigs = []json.HexBytes{sig1, sig2}
-       err = materializeWitnesses(tpl)
+       err = MaterializeWitnesses(tpl)
        if err != nil {
                testutil.FatalErr(t, err)
        }
index 8daee37..ed78b37 100755 (executable)
@@ -4,6 +4,7 @@ import (
        "context"
 
        "github.com/bytom/crypto/ed25519/chainkd"
+       chainjson "github.com/bytom/encoding/json"
        "github.com/bytom/errors"
 )
 
@@ -11,10 +12,10 @@ import (
 // a signature for a given xpub, derivation path, and hash.
 type SignFunc func(context.Context, chainkd.XPub, [][]byte, [32]byte, string) ([]byte, error)
 
-// materializeWitnesses takes a filled in Template and "materializes"
+// MaterializeWitnesses takes a filled in Template and "materializes"
 // each witness component, turning it into a vector of arguments for
 // the tx's input witness, creating a fully-signed transaction.
-func materializeWitnesses(txTemplate *Template) error {
+func MaterializeWitnesses(txTemplate *Template) error {
        msg := txTemplate.Transaction
 
        if msg == nil {
@@ -42,3 +43,31 @@ func materializeWitnesses(txTemplate *Template) error {
 
        return nil
 }
+
+func signedCount(signs []chainjson.HexBytes) (count int) {
+       for _, sign := range signs {
+               if len(sign) > 0 {
+                       count++
+               }
+       }
+       return
+}
+
+// SignProgress check is all the sign requirement are satisfy
+func SignProgress(txTemplate *Template) bool {
+       for _, sigInst := range txTemplate.SigningInstructions {
+               for _, wc := range sigInst.WitnessComponents {
+                       switch sw := wc.(type) {
+                       case *SignatureWitness:
+                               if signedCount(sw.Sigs) < sw.Quorum {
+                                       return false
+                               }
+                       case *RawTxSigWitness:
+                               if signedCount(sw.Sigs) < sw.Quorum {
+                                       return false
+                               }
+                       }
+               }
+       }
+       return true
+}
index 063d02a..75057c4 100644 (file)
@@ -9,6 +9,7 @@ import (
 
        "github.com/bytom/blockchain/account"
        "github.com/bytom/blockchain/pseudohsm"
+       "github.com/bytom/blockchain/txbuilder"
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/protocol/bc/legacy"
        "github.com/bytom/protocol/validation"
@@ -57,7 +58,11 @@ func TestP2PKH(t *testing.T) {
                t.Fatal(err)
        }
 
-       if err := test.MockSign(tpl, hsm); err != nil {
+       if _, err := test.MockSign(tpl, hsm, "password"); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := txbuilder.MaterializeWitnesses(tpl); err != nil {
                t.Fatal(err)
        }
 
@@ -113,10 +118,87 @@ func TestP2SH(t *testing.T) {
                t.Fatal(err)
        }
 
-       if err := test.MockSign(tpl, hsm); err != nil {
+       if _, err := test.MockSign(tpl, hsm, "password"); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := txbuilder.MaterializeWitnesses(tpl); err != nil {
+               t.Fatal(err)
+       }
+
+       if _, _, err = validation.ValidateTx(legacy.MapTx(tx), test.MockBlock()); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestMutilNodeSign(t *testing.T) {
+       dirPath, err := ioutil.TempDir(".", "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dirPath)
+
+       testDB := dbm.NewDB("testdb", "leveldb", "temp")
+       defer os.RemoveAll("temp")
+
+       chain, err := test.MockChain(testDB)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       accountManager := account.NewManager(testDB, chain)
+       hsm, err := pseudohsm.New(dirPath)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       xpub1, err := hsm.XCreate("test_pub1", "password1")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       xpub2, err := hsm.XCreate("test_pub2", "password2")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       testAccount, err := accountManager.Create(nil, []chainkd.XPub{xpub1.XPub, xpub2.XPub}, 2, "testAccount", nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       controlProg, err := accountManager.CreateAddress(nil, testAccount.ID, false)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       utxo := test.MockUTXO(controlProg)
+       tpl, tx, err := test.MockTx(utxo, testAccount)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if finishSign, err := test.MockSign(tpl, hsm, "password"); err != nil {
                t.Fatal(err)
+       } else if finishSign == true {
+               t.Fatal("sign progress is finish, but either xpub1 nor xpub2 is signed")
        }
 
+       if finishSign, err := test.MockSign(tpl, hsm, "password1"); err != nil {
+               t.Fatal(err)
+       } else if finishSign == true {
+               t.Fatal("sign progress is finish, but xpub2 is not signed")
+       }
+
+       if finishSign, err := test.MockSign(tpl, hsm, "password2"); err != nil {
+               t.Fatal(err)
+       } else if finishSign == false {
+               t.Fatal("sign progress is not finish,  but both xpub1 and xpub2 is signed")
+       }
+
+       if err := txbuilder.MaterializeWitnesses(tpl); err != nil {
+               t.Fatal(err)
+       }
        if _, _, err = validation.ValidateTx(legacy.MapTx(tx), test.MockBlock()); err != nil {
                t.Fatal(err)
        }
index b8d88b5..d746d86 100644 (file)
@@ -62,11 +62,14 @@ func MockTx(utxo *account.UTXO, testAccount *account.Account) (*txbuilder.Templa
        return b.Build()
 }
 
-func MockSign(tpl *txbuilder.Template, hsm *pseudohsm.HSM) error {
-       return txbuilder.Sign(nil, tpl, nil, []string{"password", "password"}, func(_ context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
-               sigBytes, err := hsm.XSign(xpub, path, data[:], password)
-               return sigBytes, err
+func MockSign(tpl *txbuilder.Template, hsm *pseudohsm.HSM, password string) (bool, error) {
+       err := txbuilder.Sign(nil, tpl, nil, password, func(_ context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
+               return hsm.XSign(xpub, path, data[:], password)
        })
+       if err != nil {
+               return false, err
+       }
+       return txbuilder.SignProgress(tpl), nil
 }
 
 // Mock block