diff --git a/account_create_transaction.go b/account_create_transaction.go index 27e7686cc..e393fdd33 100644 --- a/account_create_transaction.go +++ b/account_create_transaction.go @@ -31,6 +31,7 @@ func (transaction AccountCreateTransaction) SetKey(publicKey Ed25519PublicKey) A protoKey := hedera_proto.Key_Ed25519{Ed25519: publicKey.keyData} transaction.body.Key = &hedera_proto.Key{Key: &protoKey} + return transaction } @@ -48,7 +49,7 @@ func (transaction AccountCreateTransaction) validate() error { return nil } -func (transaction AccountCreateTransaction) Build() (Transaction, error) { +func (transaction AccountCreateTransaction) Build() (*Transaction, error) { if err := transaction.validate(); err != nil { return nil, err } diff --git a/examples/create_account/main.go b/examples/create_account/main.go index 742e4ff99..1664bc424 100644 --- a/examples/create_account/main.go +++ b/examples/create_account/main.go @@ -32,7 +32,12 @@ func main() { operatorPrivateKey, ) - newKey := hedera.NewEd25519PrivateKey() + newKey, err := hedera.GenerateEd25519PrivateKey() + + if err != nil { + panic(err) + } + newPublicKey := newKey.PublicKey() tx, err := hedera.NewAccountCreateTransaction(client). diff --git a/transaction.go b/transaction.go index b7abab594..5c3c3bef8 100644 --- a/transaction.go +++ b/transaction.go @@ -1,6 +1,17 @@ package hedera -import "strings" +import ( + "errors" + "strings" + "time" + + "github.com/hashgraph/hedera-sdk-go/hedera_proto" +) + +const receiptRetryDelay = 500 +const receiptInitialDelay = 1000 + +const prefixLen = 6 type ErrorTransactionValidation struct { Messages []string @@ -11,15 +22,106 @@ func (e *ErrorTransactionValidation) Error() string { return "The following requirements were not met: \n" + strings.Join(e.Messages, "\n") } -// fixme: this should probably be a struct with those functions implementat on it -type Transaction interface { - Execute() error - ExecuteForReceipt() (TransactionReceipt, error) -} - type TransactionBuilder interface { SetMaxTransactionFee(uint64) *TransactionBuilder SetMemo(string) validate() ErrorTransactionValidation Build() (Transaction, error) } + +type TransactionID struct { + Account AccountID + ValidStartSeconds uint64 + ValidStartNanos uint32 +} + +func generateTransactionID(accountID AccountID) TransactionID { + now := time.Now() + + return TransactionID{ + accountID, + uint64(now.Unix()), + uint32(now.UnixNano() - (now.Unix() * 1e+9)), + } +} + +type Transaction struct { + Kind TransactionKind + client *Client + inner hedera_proto.Transaction +} + +func (transaction Transaction) AddSignature(signature []byte, publicKey Ed25519PublicKey) Transaction { + signaturePair := hedera_proto.SignaturePair{ + PubKeyPrefix: publicKey.keyData, + Signature: &hedera_proto.SignaturePair_Ed25519{ + Ed25519: signature, + }, + } + + sigmap := transaction.inner.GetSigMap() + + if sigmap == nil { + sigmap = &hedera_proto.SignatureMap{} + } + + sigmap.SigPair = append(sigmap.SigPair, &signaturePair) + + transaction.inner.SigMap = sigmap + + return transaction +} + +func (transaction Transaction) Sign(privateKey Ed25519PrivateKey) Transaction { + signature := privateKey.Sign(transaction.inner.GetBodyBytes()) + + return transaction.AddSignature(signature, privateKey.PublicKey()) +} + +func (transaction Transaction) getReceipt() (*TransactionReceipt, error) { + return nil, nil +} + +func (transaction Transaction) Execute() (*TransactionID, error) { + // fixme: proper error handling + if transaction.client == nil { + return nil, errors.New("No client was provided on this transaction") + } + + txID := generateTransactionID(transaction.client.operator.accountID) + + body := transaction.inner.GetBody() + + body.TransactionID = &hedera_proto.TransactionID{ + TransactionValidStart: &hedera_proto.Timestamp{ + Seconds: int64(txID.ValidStartSeconds), + Nanos: int32(txID.ValidStartNanos), + }, + AccountID: &hedera_proto.AccountID{ + ShardNum: int64(txID.Account.Shard), + RealmNum: int64(txID.Account.Realm), + AccountNum: int64(txID.Account.Account), + }, + } + + // todo: use response and handle precheck codes + _, error := transaction.Kind.execute(*transaction.client, transaction.inner) + + // todo: handle result errors + if error != nil { + return nil, error + } + + return &txID, nil +} + +func (transaction Transaction) ExecuteForReceipt() (*TransactionReceipt, error) { + _, err := transaction.Execute() + + if err != nil { + return nil, err + } + + // todo: add receipts + return nil, nil +} diff --git a/transaction_kind.go b/transaction_kind.go new file mode 100644 index 000000000..c590cb6b4 --- /dev/null +++ b/transaction_kind.go @@ -0,0 +1,26 @@ +package hedera + +import ( + "context" + "errors" + + "github.com/hashgraph/hedera-sdk-go/hedera_proto" +) + +type TransactionKind int + +const ( + CryptoCreateAccount TransactionKind = iota + CryptoTransfer +) + +func (tk TransactionKind) execute(client Client, tx hedera_proto.Transaction) (*hedera_proto.TransactionResponse, error) { + switch tk { + case CryptoCreateAccount: + return hedera_proto.NewCryptoServiceClient(client.conn).CreateAccount(context.TODO(), &tx) + case CryptoTransfer: + return nil, errors.New("not implemented yet") + default: + return nil, errors.New("invalid kind provided") + } +} diff --git a/transaction_receipt.go b/transaction_receipt.go index 93250e8f8..a2431eb46 100644 --- a/transaction_receipt.go +++ b/transaction_receipt.go @@ -26,14 +26,14 @@ func TransactionReceiptFromResponse(response hedera_proto.Response) (*Transactio return &receipt, nil } -func (transactionReceipt TransactionReceipt) AccountID() AccountID { +func (transactionReceipt TransactionReceipt) AccountID() *AccountID { internalID := transactionReceipt.inner.AccountID if internalID == nil { return nil } - return AccountID{ + return &AccountID{ uint64(internalID.ShardNum), uint64(internalID.RealmNum), uint64(internalID.AccountNum),