This repository has been archived by the owner on Mar 21, 2024. It is now read-only.
/
signature_witness.go
134 lines (120 loc) · 4.24 KB
/
signature_witness.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package txbuilder
import (
"context"
"encoding/json"
log "github.com/sirupsen/logrus"
"github.com/bytom/bytom/crypto/ed25519/chainkd"
"github.com/bytom/bytom/crypto/sha3pool"
chainjson "github.com/bytom/bytom/encoding/json"
"github.com/bytom/bytom/errors"
"github.com/bytom/bytom/protocol/vm"
)
type (
// SignatureWitness is a sign struct
SignatureWitness struct {
// Quorum is the number of signatures required.
Quorum int `json:"quorum"`
// Keys are the identities of the keys to sign with.
Keys []keyID `json:"keys"`
// Program is the predicate part of the signature program, whose hash is what gets
// signed. If empty, it is computed during Sign from the outputs
// and the current input of the transaction.
Program chainjson.HexBytes `json:"program"`
// Sigs are signatures of Program made from each of the Keys
// during Sign.
Sigs []chainjson.HexBytes `json:"signatures"`
}
keyID struct {
XPub chainkd.XPub `json:"xpub"`
DerivationPath []chainjson.HexBytes `json:"derivation_path"`
}
)
// 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
// sw.Program as it can from the overlapping set of keys in sw.Keys.
//
// If sw.Program is empty, it is populated with an _inferred_ predicate:
// a program committing to aspects of the current
// transaction. Specifically, the program commits to:
// - the mintime and maxtime of the transaction (if non-zero)
// - the outputID of the current input
// - the assetID, amount, control program of each output.
func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint32, 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
// constraints derived from the existing outputs and current input.
if len(sw.Program) == 0 {
var err error
sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
if err != nil {
return err
}
if len(sw.Program) == 0 {
return ErrEmptyProgram
}
}
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
// sigs already present.
newSigs := make([]chainjson.HexBytes, len(sw.Keys))
copy(newSigs, sw.Sigs)
sw.Sigs = newSigs
}
var h [32]byte
sha3pool.Sum256(h[:], sw.Program)
for i, keyID := range sw.Keys {
if len(sw.Sigs[i]) > 0 {
// Already have a signature for this key
continue
}
path := make([][]byte, len(keyID.DerivationPath))
for i, p := range keyID.DerivationPath {
path[i] = p
}
sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth)
if err != nil {
log.WithFields(log.Fields{"module": logModule, "err": err}).Warningf("computing signature %d", i)
continue
}
// This break is ordered to avoid signing transaction successfully only once for a multiple-sign account
// that consist of different keys by the same password. Exit immediately when the signature is success,
// it means that only one signature will be successful in the loop for this multiple-sign account.
sw.Sigs[i] = sigBytes
break
}
return nil
}
func (sw SignatureWitness) materialize(args *[][]byte) error {
// This is the value of N for the CHECKPREDICATE call. The code
// assumes that everything already in the arg list before this call
// to Materialize is input to the signature program, so N is
// len(*args).
*args = append(*args, vm.Int64Bytes(int64(len(*args))))
var nsigs int
for i := 0; i < len(sw.Sigs) && nsigs < sw.Quorum; i++ {
if len(sw.Sigs[i]) > 0 {
*args = append(*args, sw.Sigs[i])
nsigs++
}
}
*args = append(*args, sw.Program)
return nil
}
// MarshalJSON convert struct to json
func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
obj := struct {
Type string `json:"type"`
Quorum int `json:"quorum"`
Keys []keyID `json:"keys"`
Sigs []chainjson.HexBytes `json:"signatures"`
}{
Type: "signature",
Quorum: sw.Quorum,
Keys: sw.Keys,
Sigs: sw.Sigs,
}
return json.Marshal(obj)
}