/
transfer.go
102 lines (93 loc) · 3.17 KB
/
transfer.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
package client
import (
"context"
"errors"
"fmt"
"time"
"golang.org/x/crypto/ed25519"
"github.com/the729/go-libra/generated/pbac"
"github.com/the729/go-libra/language/stdscript"
"github.com/the729/go-libra/types"
)
// NewRawP2PTransaction creates a new serialized raw transaction bytes corresponding to a
// peer-to-peer Libra coin transaction.
func NewRawP2PTransaction(
senderAddress, receiverAddress types.AccountAddress, receiverAuthKeyPrefix []byte,
senderSequenceNumber uint64,
amount, maxGasAmount, gasUnitPrice uint64,
expiration time.Time,
) (*types.RawTransaction, error) {
txn := &types.RawTransaction{
Sender: senderAddress,
SequenceNumber: senderSequenceNumber,
Payload: &types.TxnPayloadScript{
Code: stdscript.PeerToPeerTransfer,
TyArgs: []types.TypeTag{types.LBRTypeTag()},
Args: []types.TransactionArgument{
types.TxnArgAddress(receiverAddress),
types.TxnArgBytes(receiverAuthKeyPrefix),
types.TxnArgU64(amount),
},
},
MaxGasAmount: maxGasAmount,
GasUnitPrice: gasUnitPrice,
GasSpecifier: types.LBRTypeTag(),
ExpirationTime: uint64(expiration.Unix()),
}
return txn, nil
}
// SubmitRawTransaction signes and submits a raw transaction.
// It returns the expected sequence number of this transaction.
func (c *Client) SubmitRawTransaction(ctx context.Context, rawTxn *types.RawTransaction, privateKey ed25519.PrivateKey) (uint64, error) {
signedTxn, err := rawTxn.Sign(privateKey)
if err != nil {
return 0, fmt.Errorf("cannot sign transaction: %v", err)
}
pbSignedTxn, _ := signedTxn.ToProto()
resp, err := c.ac.SubmitTransaction(ctx, &pbac.SubmitTransactionRequest{
Transaction: pbSignedTxn,
})
if err != nil {
return 0, fmt.Errorf("submit transaction error: %v", err)
}
// log.Printf("Result: ")
// spew.Dump(resp)
if vmStatus := resp.GetVmStatus(); vmStatus != nil {
return 0, fmt.Errorf("vm error: %s", vmStatus)
}
if mpStatus := resp.GetMempoolStatus(); mpStatus != nil {
return 0, fmt.Errorf("mempool error: %s", mpStatus)
}
if acStatus := resp.GetAcStatus(); acStatus.Code != pbac.AdmissionControlStatusCode_Accepted {
return 0, fmt.Errorf("ac error: %s", acStatus)
}
return rawTxn.SequenceNumber + 1, nil
}
// PollSequenceUntil blocks to repeatedly poll the sequence number of a specific account, until the sequence number
// is greater or equal to specified target sequence number, or the ledger state passes specified expiration time.
func (c *Client) PollSequenceUntil(ctx context.Context, addr types.AccountAddress, targetSeq uint64, expiration time.Time) error {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
paccount, err := c.QueryAccountState(ctx, addr)
if err != nil {
return err
}
ledgerInfo := paccount.GetLedgerInfo()
if !paccount.IsNil() {
resource, err := paccount.GetAccountBlob().GetLibraAccountResource()
if err != nil {
return err
}
seq := resource.SequenceNumber
// log.Printf("sequence number: %d, ledger version: %d", seq, ledgerInfo.GetVersion())
if seq >= targetSeq {
return nil
}
}
if ledgerInfo.GetTimestampUsec() > uint64(expiration.Unix()+1)*1000000 {
break
}
}
return errors.New("expired")
}