-
Notifications
You must be signed in to change notification settings - Fork 77
/
transaction.go
118 lines (108 loc) · 3.06 KB
/
transaction.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
package oracle
import (
"sync"
"time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
type (
incompleteTx struct {
sync.RWMutex
// isSent is true if tx has been already broadcasted.
isSent bool
// attempts is how many times the request was processed.
attempts int
// time is the time when the request was last processed.
time time.Time
// request is an oracle request.
request *state.OracleRequest
// tx is an oracle response transaction.
tx *transaction.Transaction
// sigs contains a signature from every oracle node.
sigs map[string]*txSignature
// backupTx is a backup transaction.
backupTx *transaction.Transaction
// backupSigs contains signatures of backup tx.
backupSigs map[string]*txSignature
}
txSignature struct {
// pub is a cached public key.
pub *keys.PublicKey
// ok is true if the signature was verified.
ok bool
// sig is tx signature.
sig []byte
}
)
func newIncompleteTx() *incompleteTx {
return &incompleteTx{
sigs: make(map[string]*txSignature),
backupSigs: make(map[string]*txSignature),
}
}
func (t *incompleteTx) reverifyTx(net netmode.Magic) {
txHash := hash.NetSha256(uint32(net), t.tx)
backupHash := hash.NetSha256(uint32(net), t.backupTx)
for pub, sig := range t.sigs {
if !sig.ok {
sig.ok = sig.pub.Verify(sig.sig, txHash.BytesBE())
if !sig.ok && sig.pub.Verify(sig.sig, backupHash.BytesBE()) {
t.backupSigs[pub] = &txSignature{
pub: sig.pub,
ok: true,
sig: sig.sig,
}
}
}
}
}
func (t *incompleteTx) addResponse(pub *keys.PublicKey, sig []byte, isBackup bool) {
tx, sigs := t.tx, t.sigs
if isBackup {
tx, sigs = t.backupTx, t.backupSigs
}
sigs[string(pub.Bytes())] = &txSignature{
pub: pub,
ok: tx != nil,
sig: sig,
}
}
// finalize checks if either main or backup tx has sufficient number of signatures and returns
// tx and bool value indicating if it is ready to be broadcasted.
func (t *incompleteTx) finalize(oracleNodes keys.PublicKeys, backupOnly bool) (*transaction.Transaction, bool) {
if !backupOnly && finalizeTx(oracleNodes, t.tx, t.sigs) {
return t.tx, true
}
return t.backupTx, finalizeTx(oracleNodes, t.backupTx, t.backupSigs)
}
func finalizeTx(oracleNodes keys.PublicKeys, tx *transaction.Transaction, txSigs map[string]*txSignature) bool {
if tx == nil {
return false
}
m := smartcontract.GetDefaultHonestNodeCount(len(oracleNodes))
sigs := make([][]byte, 0, m)
for _, pub := range oracleNodes {
sig, ok := txSigs[string(pub.Bytes())]
if ok && sig.ok {
sigs = append(sigs, sig.sig)
if len(sigs) == m {
break
}
}
}
if len(sigs) != m {
return false
}
w := io.NewBufBinWriter()
for i := range sigs {
emit.Bytes(w.BinWriter, sigs[i])
}
tx.Scripts[1].InvocationScript = w.Bytes()
return true
}