update/finish app1 go and md

This commit is contained in:
Ethan Buchman 2018-06-27 07:51:46 -04:00
parent 354f9760f8
commit fc81c14a16
2 changed files with 141 additions and 49 deletions

View File

@ -243,7 +243,7 @@ Note this handler has unfettered access to the store specified by the capability
For this first example, we will define a simple account that is JSON encoded: For this first example, we will define a simple account that is JSON encoded:
```go ```go
type acc struct { type app1Account struct {
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
} }
``` ```
@ -256,35 +256,105 @@ Now we're ready to handle the MsgSend:
```go ```go
// Handle MsgSend. // Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result { func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
// NOTE: from, to, and amount were already validated // Load the store.
store := ctx.KVStore(key) store := ctx.KVStore(key)
bz := store.Get(msg.From)
if bz == nil { // Debit from the sender.
// TODO if res := handleFrom(store, msg.From, msg.Amount); !res.IsOK() {
return res
} }
var acc acc // Credit the receiver.
err := json.Unmarshal(bz, &acc) if res := handleTo(store, msg.To, msg.Amount); !res.IsOK() {
if err != nil { return res
// InternalError
} }
// TODO: finish the logic // Return a success (Code 0).
// Add list of key-value pair descriptors ("tags").
return sdk.Result{ return sdk.Result{
// TODO: Tags Tags: msg.Tags(),
} }
} }
``` ```
The handler is straight forward: The handler is straight forward. We first load the KVStore from the context using the granted capability key.
Then we make two state transitions: one for the sender, one for the receiver.
Each one involves JSON unmarshalling the account bytes from the store, mutating
the `Coins`, and JSON marshalling back into the store:
- get the KVStore from the context using the granted capability key ```go
- lookup the From address in the KVStore, and JSON unmarshal it into an `acc`, func handleFrom(store sdk.KVStore, from sdk.Address, amt sdk.Coins) sdk.Result {
- check that the account balance is greater than the `msg.Amount` // Get sender account from the store.
- transfer the `msg.Amount` accBytes := store.Get(from)
if accBytes == nil {
// Account was not added to store. Return the result of the error.
return sdk.NewError(2, 101, "Account not added to store").Result()
}
// Unmarshal the JSON account bytes.
var acc app1Account
err := json.Unmarshal(accBytes, &acc)
if err != nil {
// InternalError
return sdk.ErrInternal("Error when deserializing account").Result()
}
// Deduct msg amount from sender account.
senderCoins := acc.Coins.Minus(amt)
// If any coin has negative amount, return insufficient coins error.
if !senderCoins.IsNotNegative() {
return sdk.ErrInsufficientCoins("Insufficient coins in account").Result()
}
// Set acc coins to new amount.
acc.Coins = senderCoins
// Encode sender account.
accBytes, err = json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated sender account
store.Set(from, accBytes)
return sdk.Result{}
}
func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
// Add msg amount to receiver account
accBytes := store.Get(to)
var acc app1Account
if accBytes == nil {
// Receiver account does not already exist, create a new one.
acc = app1Account{}
} else {
// Receiver account already exists. Retrieve and decode it.
err := json.Unmarshal(accBytes, &acc)
if err != nil {
return sdk.ErrInternal("Account decoding error").Result()
}
}
// Add amount to receiver's old coins
receiverCoins := acc.Coins.Plus(amt)
// Update receiver account
acc.Coins = receiverCoins
// Encode receiver account
accBytes, err := json.Marshal(acc)
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated receiver account
store.Set(to, accBytes)
return sdk.Result{}
}
```
And that's that! And that's that!

View File

@ -96,6 +96,12 @@ func (msg MsgSend) GetSigners() []sdk.Address {
return []sdk.Address{msg.From} return []sdk.Address{msg.From}
} }
// Returns the sdk.Tags for the message
func (msg MsgSend) Tags() sdk.Tags {
return sdk.NewTags("sender", []byte(msg.From.String())).
AppendTag("receiver", []byte(msg.To.String()))
}
//------------------------------------------------------------------ //------------------------------------------------------------------
// Handler for the message // Handler for the message
@ -112,78 +118,99 @@ func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
} }
// Handle MsgSend. // Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result { func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
// NOTE: from, to, and amount were already validated // Load the store.
store := ctx.KVStore(key) store := ctx.KVStore(key)
// deduct msg amount from sender account // Debit from the sender.
bz := store.Get(msg.From) if res := handleFrom(store, msg.From, msg.Amount); !res.IsOK() {
if bz == nil { return res
}
// Credit the receiver.
if res := handleTo(store, msg.To, msg.Amount); !res.IsOK() {
return res
}
// Return a success (Code 0).
// Add list of key-value pair descriptors ("tags").
return sdk.Result{
Tags: msg.Tags(),
}
}
func handleFrom(store sdk.KVStore, from sdk.Address, amt sdk.Coins) sdk.Result {
// Get sender account from the store.
accBytes := store.Get(from)
if accBytes == nil {
// Account was not added to store. Return the result of the error. // Account was not added to store. Return the result of the error.
return sdk.NewError(2, 101, "Account not added to store").Result() return sdk.NewError(2, 101, "Account not added to store").Result()
} }
var acc account // Unmarshal the JSON account bytes.
err := json.Unmarshal(bz, &acc) var acc app1Account
err := json.Unmarshal(accBytes, &acc)
if err != nil { if err != nil {
// InternalError // InternalError
return sdk.ErrInternal("Error when deserializing account").Result() return sdk.ErrInternal("Error when deserializing account").Result()
} }
// TODO: finish the logic // Deduct msg amount from sender account.
senderCoins := acc.Coins.Minus(msg.Amount) senderCoins := acc.Coins.Minus(amt)
// If any coin has negative amount, return insufficient coins error. // If any coin has negative amount, return insufficient coins error.
if !senderCoins.IsNotNegative() { if !senderCoins.IsNotNegative() {
return sdk.ErrInsufficientCoins("Insufficient coins in account").Result() return sdk.ErrInsufficientCoins("Insufficient coins in account").Result()
} }
// set acc coins to new amount // Set acc coins to new amount.
acc.Coins = senderCoins acc.Coins = senderCoins
// Encode sender account // Encode sender account.
val, err := json.Marshal(acc) accBytes, err = json.Marshal(acc)
if err != nil { if err != nil {
return sdk.ErrInternal("Account encoding error").Result() return sdk.ErrInternal("Account encoding error").Result()
} }
// Update store with updated sender account // Update store with updated sender account
store.Set(msg.From, val) store.Set(from, accBytes)
return sdk.Result{}
}
func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
// Add msg amount to receiver account // Add msg amount to receiver account
bz = store.Get(msg.To) accBytes := store.Get(to)
var acc2 account var acc app1Account
if bz == nil { if accBytes == nil {
// Receiver account does not already exist, create a new one. // Receiver account does not already exist, create a new one.
acc2 = account{} acc = app1Account{}
} else { } else {
// Receiver account already exists. Retrieve and decode it. // Receiver account already exists. Retrieve and decode it.
err = json.Unmarshal(bz, &acc2) err := json.Unmarshal(accBytes, &acc)
if err != nil { if err != nil {
return sdk.ErrInternal("Account decoding error").Result() return sdk.ErrInternal("Account decoding error").Result()
} }
} }
// Add amount to receiver's old coins // Add amount to receiver's old coins
receiverCoins := acc2.Coins.Plus(msg.Amount) receiverCoins := acc.Coins.Plus(amt)
// Update receiver account // Update receiver account
acc2.Coins = receiverCoins acc.Coins = receiverCoins
// Encode receiver account // Encode receiver account
val, err = json.Marshal(acc2) accBytes, err := json.Marshal(acc)
if err != nil { if err != nil {
return sdk.ErrInternal("Account encoding error").Result() return sdk.ErrInternal("Account encoding error").Result()
} }
store.Set(msg.To, val) // Update store with updated receiver account
store.Set(to, accBytes)
return sdk.Result{ return sdk.Result{}
// TODO: Tags
}
} }
type account struct { type app1Account struct {
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
} }
@ -200,11 +227,6 @@ func (tx app1Tx) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx.MsgSend} return []sdk.Msg{tx.MsgSend}
} }
// TODO: remove the need for this
func (tx app1Tx) GetMemo() string {
return ""
}
// JSON decode MsgSend. // JSON decode MsgSend.
func txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) { func txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app1Tx var tx app1Tx