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 {
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) {
}
//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
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{})
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)
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())
}
// 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
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)
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)
"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
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
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
}
return nil
}
+// MarshalJSON convert struct to json
func (sw RawTxSigWitness) MarshalJSON() ([]byte, error) {
obj := struct {
Type string `json:"type"`
"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"
)
type (
+ // SignatureWitness is a sign struct
SignatureWitness struct {
// Quorum is the number of signatures required.
Quorum int `json:"quorum"`
}
)
+// 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
// - 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
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
}
return nil
}
+// MarshalJSON convert struct to json
func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
obj := struct {
Type string `json:"type"`
"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")
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) {
}
}
}
- return materializeWitnesses(tpl)
+ return nil
}
func checkBlankCheck(tx *legacy.TxData) error {
},
},
}}
- err = materializeWitnesses(tpl)
+ err = MaterializeWitnesses(tpl)
if err != nil {
testutil.FatalErr(t, err)
}
// 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)
}
"context"
"github.com/bytom/crypto/ed25519/chainkd"
+ chainjson "github.com/bytom/encoding/json"
"github.com/bytom/errors"
)
// 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 {
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
+}
"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"
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)
}
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)
}
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