@ -1,74 +0,0 @@ | |||
package account | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/go-merkle" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
) | |||
// Signable is an interface for all signable things. | |||
// It typically removes signatures before serializing. | |||
type Signable interface { | |||
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) | |||
} | |||
// SignBytes is a convenience method for getting the bytes to sign of a Signable. | |||
func SignBytes(chainID string, o Signable) []byte { | |||
buf, n, err := new(bytes.Buffer), new(int64), new(error) | |||
o.WriteSignBytes(chainID, buf, n, err) | |||
if *err != nil { | |||
PanicCrisis(err) | |||
} | |||
return buf.Bytes() | |||
} | |||
// HashSignBytes is a convenience method for getting the hash of the bytes of a signable | |||
func HashSignBytes(chainID string, o Signable) []byte { | |||
return merkle.SimpleHashFromBinary(SignBytes(chainID, o)) | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Account resides in the application state, and is mutated by transactions | |||
// on the blockchain. | |||
// Serialized by wire.[read|write]Reflect | |||
type Account struct { | |||
Address []byte `json:"address"` | |||
PubKey PubKey `json:"pub_key"` | |||
Sequence int `json:"sequence"` | |||
Balance int64 `json:"balance"` | |||
Code []byte `json:"code"` // VM code | |||
StorageRoot []byte `json:"storage_root"` // VM storage merkle root. | |||
Permissions ptypes.AccountPermissions `json:"permissions"` | |||
} | |||
func (acc *Account) Copy() *Account { | |||
accCopy := *acc | |||
return &accCopy | |||
} | |||
func (acc *Account) String() string { | |||
if acc == nil { | |||
return "nil-Account" | |||
} | |||
return fmt.Sprintf("Account{%X:%v B:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, acc.Balance, len(acc.Code), acc.StorageRoot, acc.Permissions) | |||
} | |||
func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) { | |||
wire.WriteBinary(o.(*Account), w, n, err) | |||
} | |||
func AccountDecoder(r io.Reader, n *int64, err *error) interface{} { | |||
return wire.ReadBinary(&Account{}, r, n, err) | |||
} | |||
var AccountCodec = wire.Codec{ | |||
Encode: AccountEncoder, | |||
Decode: AccountDecoder, | |||
} |
@ -1,85 +0,0 @@ | |||
package account | |||
import ( | |||
"github.com/tendermint/ed25519" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/go-wire" | |||
) | |||
type PrivAccount struct { | |||
Address []byte `json:"address"` | |||
PubKey PubKey `json:"pub_key"` | |||
PrivKey PrivKey `json:"priv_key"` | |||
} | |||
func (pA *PrivAccount) Generate(index int) *PrivAccount { | |||
newPrivKey := pA.PrivKey.(PrivKeyEd25519).Generate(index) | |||
newPubKey := newPrivKey.PubKey() | |||
newAddress := newPubKey.Address() | |||
return &PrivAccount{ | |||
Address: newAddress, | |||
PubKey: newPubKey, | |||
PrivKey: newPrivKey, | |||
} | |||
} | |||
func (pA *PrivAccount) Sign(chainID string, o Signable) Signature { | |||
return pA.PrivKey.Sign(SignBytes(chainID, o)) | |||
} | |||
func (pA *PrivAccount) String() string { | |||
return Fmt("PrivAccount{%X}", pA.Address) | |||
} | |||
//---------------------------------------- | |||
// Generates a new account with private key. | |||
func GenPrivAccount() *PrivAccount { | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], CRandBytes(32)) | |||
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes) | |||
pubKey := PubKeyEd25519(*pubKeyBytes) | |||
privKey := PrivKeyEd25519(*privKeyBytes) | |||
return &PrivAccount{ | |||
Address: pubKey.Address(), | |||
PubKey: pubKey, | |||
PrivKey: privKey, | |||
} | |||
} | |||
// Generates 32 priv key bytes from secret | |||
func GenPrivKeyBytesFromSecret(secret string) []byte { | |||
return wire.BinarySha256(secret) // Not Ripemd160 because we want 32 bytes. | |||
} | |||
// Generates a new account with private key from SHA256 hash of a secret | |||
func GenPrivAccountFromSecret(secret string) *PrivAccount { | |||
privKey32 := GenPrivKeyBytesFromSecret(secret) | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], privKey32) | |||
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes) | |||
pubKey := PubKeyEd25519(*pubKeyBytes) | |||
privKey := PrivKeyEd25519(*privKeyBytes) | |||
return &PrivAccount{ | |||
Address: pubKey.Address(), | |||
PubKey: pubKey, | |||
PrivKey: privKey, | |||
} | |||
} | |||
func GenPrivAccountFromPrivKeyBytes(privKeyBytes []byte) *PrivAccount { | |||
if len(privKeyBytes) != 64 { | |||
PanicSanity(Fmt("Expected 64 bytes but got %v", len(privKeyBytes))) | |||
} | |||
var privKeyArray [64]byte | |||
copy(privKeyArray[:], privKeyBytes) | |||
pubKeyBytes := ed25519.MakePublicKey(&privKeyArray) | |||
pubKey := PubKeyEd25519(*pubKeyBytes) | |||
privKey := PrivKeyEd25519(privKeyArray) | |||
return &PrivAccount{ | |||
Address: pubKey.Address(), | |||
PubKey: pubKey, | |||
PrivKey: privKey, | |||
} | |||
} |
@ -1,70 +0,0 @@ | |||
package account | |||
import ( | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/go-common" | |||
) | |||
// PrivKey is part of PrivAccount and state.PrivValidator. | |||
type PrivKey interface { | |||
Sign(msg []byte) Signature | |||
PubKey() PubKey | |||
} | |||
// Types of PrivKey implementations | |||
const ( | |||
PrivKeyTypeEd25519 = byte(0x01) | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ PrivKey }{}, | |||
wire.ConcreteType{PrivKeyEd25519{}, PrivKeyTypeEd25519}, | |||
) | |||
//------------------------------------- | |||
// Implements PrivKey | |||
type PrivKeyEd25519 [64]byte | |||
func (key PrivKeyEd25519) Sign(msg []byte) Signature { | |||
privKeyBytes := [64]byte(key) | |||
signatureBytes := ed25519.Sign(&privKeyBytes, msg) | |||
return SignatureEd25519(*signatureBytes) | |||
} | |||
func (privKey PrivKeyEd25519) PubKey() PubKey { | |||
privKeyBytes := [64]byte(privKey) | |||
return PubKeyEd25519(*ed25519.MakePublicKey(&privKeyBytes)) | |||
} | |||
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519 := new([32]byte) | |||
privKeyBytes := [64]byte(privKey) | |||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes) | |||
return keyCurve25519 | |||
} | |||
func (privKey PrivKeyEd25519) String() string { | |||
return Fmt("PrivKeyEd25519{*****}") | |||
} | |||
// Deterministically generates new priv-key bytes from key. | |||
func (key PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { | |||
newBytes := wire.BinarySha256(struct { | |||
PrivKey [64]byte | |||
Index int | |||
}{key, index}) | |||
var newKey [64]byte | |||
copy(newKey[:], newBytes) | |||
return PrivKeyEd25519(newKey) | |||
} | |||
func GenPrivKeyEd25519() PrivKeyEd25519 { | |||
privKeyBytes := new([64]byte) | |||
copy(privKeyBytes[:32], CRandBytes(32)) | |||
ed25519.MakePublicKey(privKeyBytes) | |||
return PrivKeyEd25519(*privKeyBytes) | |||
} |
@ -1,91 +0,0 @@ | |||
package account | |||
import ( | |||
"bytes" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/ed25519/extra25519" | |||
"golang.org/x/crypto/ripemd160" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/go-common" | |||
) | |||
// PubKey is part of Account and Validator. | |||
type PubKey interface { | |||
Address() []byte | |||
VerifyBytes(msg []byte, sig Signature) bool | |||
} | |||
// Types of PubKey implementations | |||
const ( | |||
PubKeyTypeEd25519 = byte(0x01) | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ PubKey }{}, | |||
wire.ConcreteType{PubKeyEd25519{}, PubKeyTypeEd25519}, | |||
) | |||
//------------------------------------- | |||
// Implements PubKey | |||
type PubKeyEd25519 [32]byte | |||
// TODO: Slicing the array gives us length prefixing but loses the type byte. | |||
// Revisit if we add more pubkey types. | |||
// For now, we artificially append the type byte in front to give us backwards | |||
// compatibility for when the pubkey wasn't fixed length array | |||
func (pubKey PubKeyEd25519) Address() []byte { | |||
w, n, err := new(bytes.Buffer), new(int64), new(error) | |||
wire.WriteBinary(pubKey[:], w, n, err) | |||
if *err != nil { | |||
PanicCrisis(*err) | |||
} | |||
// append type byte | |||
encodedPubkey := append([]byte{1}, w.Bytes()...) | |||
hasher := ripemd160.New() | |||
hasher.Write(encodedPubkey) // does not error | |||
return hasher.Sum(nil) | |||
} | |||
// TODO: Consider returning a reason for failure, or logging a runtime type mismatch. | |||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { | |||
sig, ok := sig_.(SignatureEd25519) | |||
if !ok { | |||
return false | |||
} | |||
pubKeyBytes := [32]byte(pubKey) | |||
sigBytes := [64]byte(sig) | |||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes) | |||
} | |||
// For use with golang/crypto/nacl/box | |||
// If error, returns nil. | |||
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte { | |||
keyCurve25519, pubKeyBytes := new([32]byte), [32]byte(pubKey) | |||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes) | |||
if !ok { | |||
return nil | |||
} | |||
return keyCurve25519 | |||
} | |||
func (pubKey PubKeyEd25519) String() string { | |||
return Fmt("PubKeyEd25519{%X}", pubKey[:]) | |||
} | |||
// Must return the full bytes in hex. | |||
// Used for map keying, etc. | |||
func (pubKey PubKeyEd25519) KeyString() string { | |||
return Fmt("%X", pubKey[:]) | |||
} | |||
func (pubKey PubKeyEd25519) Equals(other PubKey) bool { | |||
if otherEd, ok := other.(PubKeyEd25519); ok { | |||
return bytes.Equal(pubKey[:], otherEd[:]) | |||
} else { | |||
return false | |||
} | |||
} |
@ -1,34 +0,0 @@ | |||
package account | |||
import ( | |||
"fmt" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/go-common" | |||
) | |||
// Signature is a part of Txs and consensus Votes. | |||
type Signature interface { | |||
IsZero() bool | |||
String() string | |||
} | |||
// Types of Signature implementations | |||
const ( | |||
SignatureTypeEd25519 = byte(0x01) | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ Signature }{}, | |||
wire.ConcreteType{SignatureEd25519{}, SignatureTypeEd25519}, | |||
) | |||
//------------------------------------- | |||
// Implements Signature | |||
type SignatureEd25519 [64]byte | |||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } | |||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } |
@ -1,71 +0,0 @@ | |||
package account | |||
import ( | |||
"bytes" | |||
"testing" | |||
"github.com/tendermint/ed25519" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/go-common" | |||
) | |||
func TestSignAndValidate(t *testing.T) { | |||
privAccount := GenPrivAccount() | |||
pubKey := privAccount.PubKey | |||
privKey := privAccount.PrivKey | |||
msg := CRandBytes(128) | |||
sig := privKey.Sign(msg) | |||
t.Logf("msg: %X, sig: %X", msg, sig) | |||
// Test the signature | |||
if !pubKey.VerifyBytes(msg, sig) { | |||
t.Errorf("Account message signature verification failed") | |||
} | |||
// Mutate the signature, just one bit. | |||
sigEd := sig.(SignatureEd25519) | |||
sigEd[0] ^= byte(0x01) | |||
sig = Signature(sigEd) | |||
if pubKey.VerifyBytes(msg, sig) { | |||
t.Errorf("Account message signature verification should have failed but passed instead") | |||
} | |||
} | |||
func TestBinaryDecode(t *testing.T) { | |||
privAccount := GenPrivAccount() | |||
pubKey := privAccount.PubKey | |||
privKey := privAccount.PrivKey | |||
msg := CRandBytes(128) | |||
sig := privKey.Sign(msg) | |||
t.Logf("msg: %X, sig: %X", msg, sig) | |||
buf, n, err := new(bytes.Buffer), new(int64), new(error) | |||
wire.WriteBinary(sig, buf, n, err) | |||
if *err != nil { | |||
t.Fatalf("Failed to write Signature: %v", err) | |||
} | |||
if len(buf.Bytes()) != ed25519.SignatureSize+1 { | |||
// 1 byte TypeByte, 64 bytes signature bytes | |||
t.Fatalf("Unexpected signature write size: %v", len(buf.Bytes())) | |||
} | |||
if buf.Bytes()[0] != SignatureTypeEd25519 { | |||
t.Fatalf("Unexpected signature type byte") | |||
} | |||
sig2, ok := wire.ReadBinary(SignatureEd25519{}, buf, n, err).(SignatureEd25519) | |||
if !ok || *err != nil { | |||
t.Fatalf("Failed to read Signature: %v", err) | |||
} | |||
// Test the signature | |||
if !pubKey.VerifyBytes(msg, sig2) { | |||
t.Errorf("Account message signature verification failed") | |||
} | |||
} |
@ -1,10 +1,10 @@ | |||
package types | |||
import ( | |||
acm "github.com/tendermint/tendermint/account" | |||
"github.com/tendermint/go-crypto" | |||
) | |||
type Validator struct { | |||
VotingPower int64 | |||
PubKey acm.PubKey | |||
PubKey crypto.PubKey | |||
} |
@ -1,10 +0,0 @@ | |||
Simulate the Economy of Texas. | |||
```bash | |||
sim_txs --priv-key "PRIV_KEY_HEX" --num-accounts 1000 --remote localhost:46657 | |||
``` | |||
The above uses the rpc-host to fetch the account state for the account with given priv-key, | |||
then deterministically generates num-accounts more accounts to spread the coins around. | |||
It's a test utility. |
@ -1,169 +0,0 @@ | |||
package main | |||
import ( | |||
"encoding/hex" | |||
"flag" | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/rpc/client" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
cclient "github.com/tendermint/tendermint/rpc/core_client" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
const Version = "0.0.1" | |||
const sleepSeconds = 1 // Every second | |||
// Parse command-line options | |||
func parseFlags() (privKeyHex string, numAccounts int, remote string) { | |||
var version bool | |||
flag.StringVar(&privKeyHex, "priv-key", "", "Private key bytes in HEX") | |||
flag.IntVar(&numAccounts, "num-accounts", 1000, "Deterministically generates this many sub-accounts") | |||
flag.StringVar(&remote, "remote", "localhost:46657", "Remote RPC host:port") | |||
flag.BoolVar(&version, "version", false, "Version") | |||
flag.Parse() | |||
if version { | |||
Exit(Fmt("sim_txs version %v", Version)) | |||
} | |||
return | |||
} | |||
func main() { | |||
// Read options | |||
privKeyHex, numAccounts, remote := parseFlags() | |||
// Print args. | |||
// fmt.Println(privKeyHex, numAccounts, remote) | |||
privKeyBytes, err := hex.DecodeString(privKeyHex) | |||
if err != nil { | |||
panic(err) | |||
} | |||
root := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) | |||
fmt.Println("Computed address: %X", root.Address) | |||
// Get root account. | |||
rootAccount, err := getAccount(remote, root.Address) | |||
if err != nil { | |||
fmt.Println(Fmt("Root account %X does not exist: %v", root.Address, err)) | |||
return | |||
} else { | |||
fmt.Println("Root account", rootAccount) | |||
} | |||
// Load all accounts | |||
accounts := make([]*acm.Account, numAccounts+1) | |||
accounts[0] = rootAccount | |||
privAccounts := make([]*acm.PrivAccount, numAccounts+1) | |||
privAccounts[0] = root | |||
for i := 1; i < numAccounts+1; i++ { | |||
privAccounts[i] = root.Generate(i) | |||
account, err := getAccount(remote, privAccounts[i].Address) | |||
if err != nil { | |||
fmt.Println("Error", err) | |||
return | |||
} else { | |||
accounts[i] = account | |||
} | |||
} | |||
// Test: send from root to accounts[1] | |||
sendTx := makeRandomTransaction(10, rootAccount.Sequence+1, root, 2, accounts) | |||
fmt.Println(sendTx) | |||
wsClient := cclient.NewWSClient("ws://" + remote + "/websocket") | |||
_, err = wsClient.Start() | |||
if err != nil { | |||
Exit(Fmt("Failed to establish websocket connection: %v", err)) | |||
} | |||
wsClient.Subscribe(types.EventStringAccInput(sendTx.Inputs[0].Address)) | |||
go func() { | |||
for { | |||
foo := <-wsClient.EventsCh | |||
fmt.Println("!!", foo) | |||
} | |||
}() | |||
err = broadcastSendTx(remote, sendTx) | |||
if err != nil { | |||
Exit(Fmt("Failed to broadcast SendTx: %v", err)) | |||
return | |||
} | |||
// Trap signal | |||
TrapSignal(func() { | |||
fmt.Println("sim_txs shutting down") | |||
}) | |||
} | |||
func getAccount(remote string, address []byte) (*acm.Account, error) { | |||
// var account *acm.Account = new(acm.Account) | |||
account, err := rpcclient.Call("http://"+remote, "get_account", []interface{}{address}, (*acm.Account)(nil)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if account.(*acm.Account) == nil { | |||
return &acm.Account{Address: address}, nil | |||
} else { | |||
return account.(*acm.Account), nil | |||
} | |||
} | |||
func broadcastSendTx(remote string, sendTx *types.SendTx) error { | |||
receipt, err := rpcclient.Call("http://"+remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil)) | |||
if err != nil { | |||
return err | |||
} | |||
fmt.Println("Broadcast receipt:", receipt) | |||
return nil | |||
} | |||
// Make a random send transaction from srcIndex to N other accounts. | |||
// balance: balance to send from input | |||
// sequence: sequence to sign with | |||
// inputPriv: input privAccount | |||
func makeRandomTransaction(balance int64, sequence int, inputPriv *acm.PrivAccount, sendCount int, accounts []*acm.Account) *types.SendTx { | |||
if sendCount >= len(accounts) { | |||
PanicSanity("Cannot make tx with sendCount >= len(accounts)") | |||
} | |||
// Remember which accounts were chosen | |||
accMap := map[string]struct{}{} | |||
accMap[string(inputPriv.Address)] = struct{}{} | |||
// Find a selection of accounts to send to | |||
outputs := []*acm.Account{} | |||
for i := 0; i < sendCount; i++ { | |||
for { | |||
idx := RandInt() % len(accounts) | |||
account := accounts[idx] | |||
if _, ok := accMap[string(account.Address)]; ok { | |||
continue | |||
} | |||
accMap[string(account.Address)] = struct{}{} | |||
outputs = append(outputs, account) | |||
break | |||
} | |||
} | |||
// Construct SendTx | |||
sendTx := types.NewSendTx() | |||
err := sendTx.AddInputWithNonce(inputPriv.PubKey, balance, sequence) | |||
if err != nil { | |||
panic(err) | |||
} | |||
for _, output := range outputs { | |||
sendTx.AddOutput(output.Address, balance/int64(len(outputs))) | |||
} | |||
// Sign SendTx | |||
sendTx.SignInput("tendermint_testnet_9", 0, inputPriv) | |||
return sendTx | |||
} |
@ -1,43 +0,0 @@ | |||
package main | |||
import ( | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/go-wire" | |||
) | |||
func gen_account() { | |||
secret, err := Prompt(`Enter your desired secret, or just hit <Enter> to generate a random account. | |||
IMPORTANT: If you don't know what a dictionary attack is, just hit Enter | |||
> `, "") | |||
if err != nil { | |||
Exit(Fmt("Not sure what happened: %v", err)) | |||
} | |||
if secret == "" { | |||
privAccount := acm.GenPrivAccount() | |||
privAccountJSONBytes := wire.JSONBytes(privAccount) | |||
fmt.Printf(`Generated a new random account! | |||
%v | |||
`, | |||
string(privAccountJSONBytes), | |||
) | |||
} else { | |||
privAccount := acm.GenPrivAccountFromSecret(secret) | |||
privAccountJSONBytes := wire.JSONBytes(privAccount) | |||
fmt.Printf(`Generated a new account from secret: [%v]! | |||
%v | |||
`, | |||
secret, | |||
string(privAccountJSONBytes), | |||
) | |||
} | |||
} |
@ -1,42 +0,0 @@ | |||
package main | |||
import ( | |||
"encoding/hex" | |||
"fmt" | |||
. "github.com/tendermint/go-common" | |||
cclient "github.com/tendermint/tendermint/rpc/core_client" | |||
) | |||
func get_account() { | |||
addrHex, err := Prompt("Enter the address of the account in HEX (e.g. 9FCBA7F840A0BFEBBE755E853C9947270A912D04):\n> ", "") | |||
if err != nil { | |||
Exit(Fmt("Error: %v", err)) | |||
} | |||
cli := cclient.NewClient("http://localhost:46657", "JSONRPC") | |||
address, err := hex.DecodeString(addrHex) | |||
if err != nil { | |||
Exit(Fmt("Address was not hex: %v", addrHex)) | |||
} | |||
res, err := cli.GetAccount(address) | |||
if res == nil { | |||
Exit(Fmt("Account does not exist: %X", address)) | |||
} | |||
if err != nil { | |||
Exit(Fmt("Error fetching account: %v", err)) | |||
} | |||
acc := res.Account | |||
fmt.Printf(` | |||
Address: %X | |||
PubKey: %v | |||
Sequence: %v | |||
Balance: %v | |||
Permissions: %v | |||
`, | |||
acc.Address, | |||
acc.PubKey, | |||
acc.Sequence, | |||
acc.Balance, | |||
acc.Permissions) | |||
} |
@ -1,120 +0,0 @@ | |||
package main | |||
import ( | |||
"encoding/hex" | |||
"fmt" | |||
"strconv" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
cclient "github.com/tendermint/tendermint/rpc/core_client" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
func getString(prompt string) string { | |||
input, err := Prompt(prompt, "") | |||
if err != nil { | |||
Exit(Fmt("Error reading input: %v", err)) | |||
} | |||
return input | |||
} | |||
func getByteSliceFromHex(prompt string) []byte { | |||
input := getString(prompt) | |||
bytes, err := hex.DecodeString(input) | |||
if err != nil { | |||
Exit(Fmt("Not in hex format: %v\nError: %v\n", input, err)) | |||
} | |||
return bytes | |||
} | |||
func getInt(prompt string) int { | |||
input := getString(prompt) | |||
i, err := strconv.Atoi(input) | |||
if err != nil { | |||
Exit(Fmt("Not a valid int64 amount: %v\nError: %v\n", input, err)) | |||
} | |||
return i | |||
} | |||
func getInt64(prompt string) int64 { | |||
return int64(getInt(prompt)) | |||
} | |||
func send_tx() { | |||
// Get PrivAccount | |||
var privAccount *acm.PrivAccount | |||
secret := getString("Enter your secret, or just hit <Enter> to enter a private key in HEX.\n> ") | |||
if secret == "" { | |||
privKeyBytes := getByteSliceFromHex("Enter your private key in HEX (e.g. E353CAD81134A301A542AEBE2D2E4EF1A64A145117EC72743AE9C9D171A4AA69F3A7DD670A9E9307AAED000D97D5B3C07D90276BFCEEDA5ED11DA089A4E87A81):\n> ") | |||
privAccount = acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) | |||
} else { | |||
// Auto-detect private key hex | |||
if len(secret) == 128 { | |||
privKeyBytes, err := hex.DecodeString(secret) | |||
if err == nil { | |||
fmt.Println("Detected priv-key bytes...") | |||
privAccount = acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) | |||
} else { | |||
fmt.Println("That's a long seed...") | |||
privAccount = acm.GenPrivAccountFromSecret(secret) | |||
} | |||
} else { | |||
privAccount = acm.GenPrivAccountFromSecret(secret) | |||
} | |||
} | |||
pubKey := privAccount.PubKey | |||
// Get account data | |||
cli := cclient.NewClient("http://localhost:46657", "JSONRPC") | |||
res, err := cli.GetAccount(privAccount.Address) | |||
if err != nil { | |||
Exit(Fmt("Error fetching account: %v", err)) | |||
} | |||
if res == nil { | |||
Exit(Fmt("No account was found with that secret/private-key")) | |||
} | |||
inputAcc := res.Account | |||
fmt.Printf(` | |||
Source account: | |||
Address: %X | |||
PubKey: %v | |||
Sequence: %v | |||
Balance: %v | |||
Permissions: %v | |||
`, | |||
inputAcc.Address, | |||
pubKey, | |||
inputAcc.Sequence, | |||
inputAcc.Balance, | |||
inputAcc.Permissions) | |||
output := getByteSliceFromHex("\nEnter the output address in HEX:\n> ") | |||
amount := getInt64("Enter the amount to send:\n> ") | |||
// Construct transaction | |||
tx := types.NewSendTx() | |||
tx.AddInputWithNonce(pubKey, amount, inputAcc.Sequence+1) | |||
tx.AddOutput(output, amount) | |||
tx.Inputs[0].Signature = privAccount.Sign(config.GetString("chain_id"), tx) | |||
fmt.Println("Signed SendTx!: ", tx) | |||
// Sign up for events | |||
wsCli := cclient.NewWSClient("ws://localhost:46657/websocket") | |||
wsCli.Start() | |||
err = wsCli.Subscribe(types.EventStringAccInput(inputAcc.Address)) | |||
if err != nil { | |||
Exit(Fmt("Error subscribing to account send event: %v", err)) | |||
} | |||
// Broadcast transaction | |||
_, err = cli.BroadcastTx(tx) | |||
if err != nil { | |||
Exit(Fmt("Error broadcasting transaction: %v", err)) | |||
} | |||
fmt.Println("Waiting for confirmation...") | |||
_ = <-wsCli.EventsCh | |||
fmt.Println("Confirmed.") | |||
} |
@ -1,312 +0,0 @@ | |||
package crawler | |||
import ( | |||
"fmt" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
cclient "github.com/tendermint/tendermint/rpc/core_client" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
const ( | |||
CheckQueueBufferSize = 100 | |||
NodeQueueBufferSize = 100 | |||
GetPeersTickerSeconds = 5 | |||
) | |||
//--------------------------------------------------------------------------------------- | |||
// crawler.Node | |||
// A node is a peer on the network | |||
type Node struct { | |||
Host string | |||
P2PPort uint16 | |||
RPCPort uint16 | |||
failed int | |||
connected bool | |||
client *NodeClient | |||
LastSeen time.Time | |||
ChainID string | |||
BlockHeight int | |||
BlockHistory map[int]time.Time // when we saw each block | |||
NetInfo *ctypes.ResultNetInfo | |||
Validator bool | |||
// other peers we heard about this peer from | |||
heardFrom map[string]struct{} | |||
} | |||
func (n *Node) Address() string { | |||
return fmt.Sprintf("%s:%d", n.Host, n.RPCPort) | |||
} | |||
// Set the basic status and chain_id info for a node from RPC responses | |||
func (n *Node) SetInfo(status *ctypes.ResultStatus, netinfo *ctypes.ResultNetInfo) { | |||
n.LastSeen = time.Now() | |||
n.ChainID = status.NodeInfo.ChainID | |||
n.BlockHeight = status.LatestBlockHeight | |||
n.NetInfo = netinfo | |||
// n.Validator | |||
} | |||
// A node client is used to talk to a node over rpc and websockets | |||
type NodeClient struct { | |||
rpc cclient.Client | |||
ws *cclient.WSClient | |||
} | |||
// Create a new client for the node at the given addr | |||
func NewNodeClient(addr string) *NodeClient { | |||
return &NodeClient{ | |||
rpc: cclient.NewClient("http://"+addr, "JSONRPC"), | |||
ws: cclient.NewWSClient("ws://" + addr + "/events"), | |||
} | |||
} | |||
// A simple wrapper for mediating access to the maps | |||
type nodeInfo struct { | |||
host string // the new nodes address | |||
port uint16 // node's listening port | |||
from string // the peer that told us about this node | |||
connected bool // move node from nodePool to nodes | |||
disconnected bool // move node from nodes to nodePool | |||
} | |||
func (ni nodeInfo) unpack() (string, uint16, string, bool, bool) { | |||
return ni.host, ni.port, ni.from, ni.connected, ni.disconnected | |||
} | |||
// crawler.Node | |||
//--------------------------------------------------------------------------------------- | |||
// crawler.Crawler | |||
// A crawler has a local node, a set of potential nodes in the nodePool, and connected nodes. | |||
// Maps are only accessed by one go-routine, mediated by the checkQueue | |||
type Crawler struct { | |||
QuitService | |||
self *Node | |||
client *NodeClient | |||
checkQueue chan nodeInfo | |||
nodePool map[string]*Node | |||
nodes map[string]*Node | |||
nodeQueue chan *Node | |||
} | |||
// Create a new Crawler using the local RPC server at addr | |||
func NewCrawler(host string, port uint16) *Crawler { | |||
crawler := &Crawler{ | |||
self: &Node{Host: host, RPCPort: port, client: NewNodeClient(fmt.Sprintf("%s:%d", host, port))}, | |||
checkQueue: make(chan nodeInfo, CheckQueueBufferSize), | |||
nodePool: make(map[string]*Node), | |||
nodes: make(map[string]*Node), | |||
nodeQueue: make(chan *Node, NodeQueueBufferSize), | |||
} | |||
crawler.QuitService = *NewQuitService(log, "Crawler", crawler) | |||
return crawler | |||
} | |||
func (c *Crawler) OnStart() error { | |||
// connect to local node first, set info, | |||
// and fire peers onto the checkQueue | |||
if err := c.pollNode(c.self); err != nil { | |||
return err | |||
} | |||
// connect to weboscket, subscribe to local events | |||
// and run the read loop to listen for new blocks | |||
_, err := c.self.client.ws.Start() | |||
if err != nil { | |||
return err | |||
} | |||
if err := c.self.client.ws.Subscribe(types.EventStringNewBlock()); err != nil { | |||
return err | |||
} | |||
go c.readLoop(c.self) | |||
// add ourselves to the nodes list | |||
c.nodes[c.self.Address()] = c.self | |||
// nodes we hear about get put on the checkQueue | |||
// by pollNode and are handled in the checkLoop. | |||
// if its a node we're not already connected to, | |||
// it gets put on the nodeQueue and | |||
// we attempt to connect in the connectLoop | |||
go c.checkLoop() | |||
go c.connectLoop() | |||
return nil | |||
} | |||
// listen for events from the node and ping it for peers on a ticker | |||
func (c *Crawler) readLoop(node *Node) { | |||
eventsCh := node.client.ws.EventsCh | |||
getPeersTicker := time.Tick(time.Second * GetPeersTickerSeconds) | |||
for { | |||
select { | |||
case eventMsg := <-eventsCh: | |||
// update the node with his new info | |||
if err := c.consumeMessage(eventMsg, node); err != nil { | |||
// lost the node, put him back on the checkQueu | |||
c.checkNode(nodeInfo{ | |||
host: node.Host, | |||
port: node.RPCPort, | |||
disconnected: true, | |||
}) | |||
} | |||
case <-getPeersTicker: | |||
if err := c.pollNode(node); err != nil { | |||
// lost the node, put him back on the checkQueu | |||
c.checkNode(nodeInfo{ | |||
host: node.Host, | |||
port: node.RPCPort, | |||
disconnected: true, | |||
}) | |||
} | |||
case <-c.Quit: | |||
return | |||
} | |||
} | |||
} | |||
func (c *Crawler) consumeMessage(eventMsg ctypes.ResultEvent, node *Node) error { | |||
block := eventMsg.Data.(*types.EventDataNewBlock).Block | |||
node.LastSeen = time.Now() | |||
node.BlockHeight = block.Height | |||
node.BlockHistory[block.Height] = node.LastSeen | |||
return nil | |||
} | |||
// check nodes against the nodePool map one at a time | |||
// acts as a mutex on nodePool and nodes | |||
func (c *Crawler) checkLoop() { | |||
for { | |||
select { | |||
case ni := <-c.checkQueue: | |||
host, port, from, connected, disconnected := ni.unpack() | |||
addr := fmt.Sprintf("%s:%d", host, port) | |||
// check if we need to swap node between maps (eg. its connected or disconnected) | |||
// NOTE: once we hear about a node, we never forget ... | |||
if connected { | |||
n, _ := c.nodePool[addr] | |||
c.nodes[addr] = n | |||
delete(c.nodePool, addr) | |||
continue | |||
} else if disconnected { | |||
n, _ := c.nodes[addr] | |||
c.nodePool[addr] = n | |||
delete(c.nodes, addr) | |||
continue | |||
} | |||
// TODO: if address is badly formed | |||
// we should punish ni.from | |||
_ = from | |||
n, ok := c.nodePool[addr] | |||
// create the node if unknown | |||
if !ok { | |||
n = &Node{Host: host, RPCPort: port} | |||
c.nodePool[addr] = n | |||
} else if n.connected { | |||
// should be removed soon | |||
continue | |||
} | |||
// queue it for connecting to | |||
c.nodeQueue <- n | |||
case <-c.Quit: | |||
return | |||
} | |||
} | |||
} | |||
// read off the nodeQueue and attempt to connect to nodes | |||
func (c *Crawler) connectLoop() { | |||
for { | |||
select { | |||
case node := <-c.nodeQueue: | |||
go c.connectToNode(node) | |||
case <-c.Quit: | |||
// close all connections | |||
for addr, node := range c.nodes { | |||
_, _ = addr, node | |||
// TODO: close conn | |||
} | |||
return | |||
} | |||
} | |||
} | |||
func (c *Crawler) connectToNode(node *Node) { | |||
addr := node.Address() | |||
node.client = NewNodeClient(addr) | |||
_, err := node.client.ws.Start() | |||
if err != nil { | |||
fmt.Println("err on ws start:", err) | |||
// set failed, return | |||
} | |||
// remove from nodePool, add to nodes | |||
c.checkNode(nodeInfo{ | |||
host: node.Host, | |||
port: node.RPCPort, | |||
connected: true, | |||
}) | |||
if err := c.pollNode(node); err != nil { | |||
// TODO: we had a good ws con | |||
// but failed on rpc?! | |||
// try again or something ... | |||
// if we still fail, report and disconnect | |||
} | |||
fmt.Println("Successfully connected to node", node.Address()) | |||
// blocks (until quit or err) | |||
c.readLoop(node) | |||
} | |||
func (c *Crawler) checkNode(ni nodeInfo) { | |||
c.checkQueue <- ni | |||
} | |||
func (c *Crawler) pollNode(node *Node) error { | |||
// get the status info | |||
status, err := node.client.rpc.Status() | |||
if err != nil { | |||
return err | |||
} | |||
// get peers and net info | |||
netinfo, err := node.client.rpc.NetInfo() | |||
if err != nil { | |||
return err | |||
} | |||
// set the info for the node | |||
node.SetInfo(status, netinfo) | |||
// fire each peer on the checkQueue | |||
for _, p := range netinfo.Peers { | |||
c.checkNode(nodeInfo{ | |||
host: p.Host, | |||
port: p.RPCPort, | |||
from: node.Address(), | |||
}) | |||
} | |||
return nil | |||
} |
@ -1,7 +0,0 @@ | |||
package crawler | |||
import ( | |||
"github.com/tendermint/log15" | |||
) | |||
var log = log15.New("module", "crawler") |
@ -1,273 +0,0 @@ | |||
package mempool | |||
import ( | |||
"fmt" | |||
"sync" | |||
"testing" | |||
"time" | |||
acm "github.com/tendermint/tendermint/account" | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
sm "github.com/tendermint/tendermint/state" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
var someAddr = []byte("ABCDEFGHIJABCDEFGHIJ") | |||
// number of txs | |||
var nTxs = 100 | |||
// what the ResetInfo should look like after ResetForBlockAndState | |||
var TestResetInfoData = ResetInfo{ | |||
Included: []Range{ | |||
Range{0, 5}, | |||
Range{10, 10}, | |||
Range{30, 5}, | |||
}, | |||
Invalid: []Range{ | |||
Range{5, 5}, | |||
Range{20, 8}, // let 28 and 29 be valid | |||
Range{35, 64}, // let 99 be valid | |||
}, | |||
} | |||
// inverse of the ResetInfo | |||
var notInvalidNotIncluded = map[int]struct{}{ | |||
28: struct{}{}, | |||
29: struct{}{}, | |||
99: struct{}{}, | |||
} | |||
func newSendTx(t *testing.T, mempool *Mempool, from *acm.PrivAccount, to []byte, amt int64) types.Tx { | |||
tx := types.NewSendTx() | |||
tx.AddInput(mempool.GetCache(), from.PubKey, amt) | |||
tx.AddOutput(to, amt) | |||
tx.SignInput(config.GetString("chain_id"), 0, from) | |||
if err := mempool.AddTx(tx); err != nil { | |||
t.Fatal(err) | |||
} | |||
return tx | |||
} | |||
func addTxs(t *testing.T, mempool *Mempool, lastAcc *acm.PrivAccount, privAccs []*acm.PrivAccount) []types.Tx { | |||
txs := make([]types.Tx, nTxs) | |||
for i := 0; i < nTxs; i++ { | |||
if _, ok := notInvalidNotIncluded[i]; ok { | |||
txs[i] = newSendTx(t, mempool, lastAcc, someAddr, 10) | |||
} else { | |||
txs[i] = newSendTx(t, mempool, privAccs[i%len(privAccs)], privAccs[(i+1)%len(privAccs)].Address, 5) | |||
} | |||
} | |||
return txs | |||
} | |||
func makeBlock(mempool *Mempool) *types.Block { | |||
txs := mempool.GetProposalTxs() | |||
var includedTxs []types.Tx | |||
for _, rid := range TestResetInfoData.Included { | |||
includedTxs = append(includedTxs, txs[rid.Start:rid.Start+rid.Length]...) | |||
} | |||
mempool.mtx.Lock() | |||
state := mempool.state | |||
state.LastBlockHeight += 1 | |||
mempool.mtx.Unlock() | |||
return &types.Block{ | |||
Header: &types.Header{ | |||
ChainID: state.ChainID, | |||
Height: state.LastBlockHeight, | |||
NumTxs: len(includedTxs), | |||
}, | |||
Data: &types.Data{ | |||
Txs: includedTxs, | |||
}, | |||
} | |||
} | |||
// Add txs. Grab chunks to put in block. All the others become invalid because of nonce errors except those in notInvalidNotIncluded | |||
func TestResetInfo(t *testing.T) { | |||
amtPerAccount := int64(100000) | |||
state, privAccs, _ := sm.RandGenesisState(6, false, amtPerAccount, 1, true, 100) | |||
mempool := NewMempool(state) | |||
lastAcc := privAccs[5] // we save him (his tx wont become invalid) | |||
privAccs = privAccs[:5] | |||
txs := addTxs(t, mempool, lastAcc, privAccs) | |||
// its actually an invalid block since we're skipping nonces | |||
// but all we care about is how the mempool responds after | |||
block := makeBlock(mempool) | |||
ri := mempool.ResetForBlockAndState(block, state) | |||
if len(ri.Included) != len(TestResetInfoData.Included) { | |||
t.Fatalf("invalid number of included ranges. Got %d, expected %d\n", len(ri.Included), len(TestResetInfoData.Included)) | |||
} | |||
if len(ri.Invalid) != len(TestResetInfoData.Invalid) { | |||
t.Fatalf("invalid number of invalid ranges. Got %d, expected %d\n", len(ri.Invalid), len(TestResetInfoData.Invalid)) | |||
} | |||
for i, rid := range ri.Included { | |||
inc := TestResetInfoData.Included[i] | |||
if rid.Start != inc.Start { | |||
t.Fatalf("Invalid start of range. Got %d, expected %d\n", inc.Start, rid.Start) | |||
} | |||
if rid.Length != inc.Length { | |||
t.Fatalf("Invalid length of range. Got %d, expected %d\n", inc.Length, rid.Length) | |||
} | |||
} | |||
txs = mempool.GetProposalTxs() | |||
if len(txs) != len(notInvalidNotIncluded) { | |||
t.Fatalf("Expected %d txs left in mempool. Got %d", len(notInvalidNotIncluded), len(txs)) | |||
} | |||
} | |||
//------------------------------------------------------------------------------------------ | |||
type TestPeer struct { | |||
sync.Mutex | |||
running bool | |||
height int | |||
t *testing.T | |||
received int | |||
txs map[string]int | |||
timeoutFail int | |||
done chan int | |||
} | |||
func newPeer(t *testing.T, state *sm.State) *TestPeer { | |||
return &TestPeer{ | |||
running: true, | |||
height: state.LastBlockHeight, | |||
t: t, | |||
txs: make(map[string]int), | |||
done: make(chan int), | |||
} | |||
} | |||
func (tp *TestPeer) IsRunning() bool { | |||
tp.Lock() | |||
defer tp.Unlock() | |||
return tp.running | |||
} | |||
func (tp *TestPeer) SetRunning(running bool) { | |||
tp.Lock() | |||
defer tp.Unlock() | |||
tp.running = running | |||
} | |||
func (tp *TestPeer) Send(chID byte, msg interface{}) bool { | |||
if tp.timeoutFail > 0 { | |||
time.Sleep(time.Second * time.Duration(tp.timeoutFail)) | |||
return false | |||
} | |||
tx := msg.(*TxMessage).Tx | |||
id := types.TxID(config.GetString("chain_id"), tx) | |||
if _, ok := tp.txs[string(id)]; ok { | |||
tp.t.Fatal("received the same tx twice!") | |||
} | |||
tp.txs[string(id)] = tp.received | |||
tp.received += 1 | |||
tp.done <- tp.received | |||
return true | |||
} | |||
func (tp *TestPeer) Get(key string) interface{} { | |||
return tp | |||
} | |||
func (tp *TestPeer) GetHeight() int { | |||
return tp.height | |||
} | |||
func TestBroadcast(t *testing.T) { | |||
state, privAccs, _ := sm.RandGenesisState(6, false, 10000, 1, true, 100) | |||
mempool := NewMempool(state) | |||
reactor := NewMempoolReactor(mempool) | |||
reactor.Start() | |||
lastAcc := privAccs[5] // we save him (his tx wont become invalid) | |||
privAccs = privAccs[:5] | |||
peer := newPeer(t, state) | |||
newBlockChan := make(chan ResetInfo) | |||
tickerChan := make(chan time.Time) | |||
go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer) | |||
// we don't broadcast any before updating | |||
fmt.Println("dont broadcast any") | |||
addTxs(t, mempool, lastAcc, privAccs) | |||
block := makeBlock(mempool) | |||
ri := mempool.ResetForBlockAndState(block, state) | |||
newBlockChan <- ri | |||
peer.height = ri.Height | |||
tickerChan <- time.Now() | |||
pullTxs(t, peer, len(mempool.txs)) // should have sent whatever txs are left (3) | |||
toBroadcast := []int{1, 3, 7, 9, 11, 12, 18, 20, 21, 28, 29, 30, 31, 34, 35, 36, 50, 90, 99, 100} | |||
for _, N := range toBroadcast { | |||
peer = resetPeer(t, reactor, mempool, state, tickerChan, newBlockChan, peer) | |||
// we broadcast N txs before updating | |||
fmt.Println("broadcast", N) | |||
addTxs(t, mempool, lastAcc, privAccs) | |||
txsToSendPerCheck = N | |||
tickerChan <- time.Now() | |||
pullTxs(t, peer, txsToSendPerCheck) // should have sent N txs | |||
block = makeBlock(mempool) | |||
ri := mempool.ResetForBlockAndState(block, state) | |||
newBlockChan <- ri | |||
peer.height = ri.Height | |||
txsToSendPerCheck = 100 | |||
tickerChan <- time.Now() | |||
left := len(mempool.txs) | |||
if N > 99 { | |||
left -= 3 | |||
} else if N > 29 { | |||
left -= 2 | |||
} else if N > 28 { | |||
left -= 1 | |||
} | |||
pullTxs(t, peer, left) // should have sent whatever txs are left that havent been sent | |||
} | |||
} | |||
func pullTxs(t *testing.T, peer *TestPeer, N int) { | |||
timer := time.NewTicker(time.Second * 2) | |||
for i := 0; i < N; i++ { | |||
select { | |||
case <-peer.done: | |||
case <-timer.C: | |||
panic(fmt.Sprintf("invalid number of received messages. Got %d, expected %d\n", i, N)) | |||
} | |||
} | |||
if N == 0 { | |||
select { | |||
case <-peer.done: | |||
t.Fatalf("should not have sent any more txs") | |||
case <-timer.C: | |||
} | |||
} | |||
} | |||
func resetPeer(t *testing.T, reactor *MempoolReactor, mempool *Mempool, state *sm.State, tickerChan chan time.Time, newBlockChan chan ResetInfo, peer *TestPeer) *TestPeer { | |||
// reset peer | |||
mempool.txs = []types.Tx{} | |||
mempool.state = state | |||
mempool.cache = sm.NewBlockCache(state) | |||
peer.SetRunning(false) | |||
tickerChan <- time.Now() | |||
peer = newPeer(t, state) | |||
go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer) | |||
return peer | |||
} |
@ -1,23 +0,0 @@ | |||
package types | |||
import ( | |||
"fmt" | |||
) | |||
//------------------------------------------------------------------------------------------------ | |||
// Some errors | |||
// permission number out of bounds | |||
type ErrInvalidPermission PermFlag | |||
func (e ErrInvalidPermission) Error() string { | |||
return fmt.Sprintf("invalid permission %d", e) | |||
} | |||
// set=false. This error should be caught and the global | |||
// value fetched for the permission by the caller | |||
type ErrValueNotSet PermFlag | |||
func (e ErrValueNotSet) Error() string { | |||
return fmt.Sprintf("the value for permission %d is not set", e) | |||
} |
@ -1,238 +0,0 @@ | |||
package types | |||
import ( | |||
"fmt" | |||
. "github.com/tendermint/go-common" | |||
) | |||
//------------------------------------------------------------------------------------------------ | |||
var ( | |||
GlobalPermissionsAddress = Zero256[:20] | |||
GlobalPermissionsAddress256 = Zero256 | |||
) | |||
// A particular permission | |||
type PermFlag uint64 | |||
// Base permission references are like unix (the index is already bit shifted) | |||
const ( | |||
// chain permissions | |||
Root PermFlag = 1 << iota // 1 | |||
Send // 2 | |||
Call // 4 | |||
CreateContract // 8 | |||
CreateAccount // 16 | |||
Bond // 32 | |||
Name // 64 | |||
// moderator permissions | |||
HasBase | |||
SetBase | |||
UnsetBase | |||
SetGlobal | |||
HasRole | |||
AddRole | |||
RmRole | |||
NumPermissions uint = 14 // NOTE Adjust this too. We can support upto 64 | |||
TopPermFlag PermFlag = 1 << (NumPermissions - 1) | |||
AllPermFlags PermFlag = TopPermFlag | (TopPermFlag - 1) | |||
DefaultPermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name | HasBase | HasRole | |||
) | |||
var ( | |||
ZeroBasePermissions = BasePermissions{0, 0} | |||
ZeroAccountPermissions = AccountPermissions{ | |||
Base: ZeroBasePermissions, | |||
} | |||
DefaultAccountPermissions = AccountPermissions{ | |||
Base: BasePermissions{ | |||
Perms: DefaultPermFlags, | |||
SetBit: AllPermFlags, | |||
}, | |||
Roles: []string{}, | |||
} | |||
) | |||
//--------------------------------------------------------------------------------------------- | |||
// Base chain permissions struct | |||
type BasePermissions struct { | |||
// bit array with "has"/"doesn't have" for each permission | |||
Perms PermFlag `json:"perms"` | |||
// bit array with "set"/"not set" for each permission (not-set should fall back to global) | |||
SetBit PermFlag `json:"set"` | |||
} | |||
// Get a permission value. ty should be a power of 2. | |||
// ErrValueNotSet is returned if the permission's set bit is off, | |||
// and should be caught by caller so the global permission can be fetched | |||
func (p *BasePermissions) Get(ty PermFlag) (bool, error) { | |||
if ty == 0 { | |||
return false, ErrInvalidPermission(ty) | |||
} | |||
if p.SetBit&ty == 0 { | |||
return false, ErrValueNotSet(ty) | |||
} | |||
return p.Perms&ty > 0, nil | |||
} | |||
// Set a permission bit. Will set the permission's set bit to true. | |||
func (p *BasePermissions) Set(ty PermFlag, value bool) error { | |||
if ty == 0 { | |||
return ErrInvalidPermission(ty) | |||
} | |||
p.SetBit |= ty | |||
if value { | |||
p.Perms |= ty | |||
} else { | |||
p.Perms &= ^ty | |||
} | |||
return nil | |||
} | |||
// Set the permission's set bit to false | |||
func (p *BasePermissions) Unset(ty PermFlag) error { | |||
if ty == 0 { | |||
return ErrInvalidPermission(ty) | |||
} | |||
p.SetBit &= ^ty | |||
return nil | |||
} | |||
// Check if the permission is set | |||
func (p *BasePermissions) IsSet(ty PermFlag) bool { | |||
if ty == 0 { | |||
return false | |||
} | |||
return p.SetBit&ty > 0 | |||
} | |||
func (p BasePermissions) String() string { | |||
return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit) | |||
} | |||
//--------------------------------------------------------------------------------------------- | |||
type AccountPermissions struct { | |||
Base BasePermissions `json:"base"` | |||
Roles []string `json:"roles"` | |||
} | |||
// Returns true if the role is found | |||
func (aP *AccountPermissions) HasRole(role string) bool { | |||
role = string(LeftPadBytes([]byte(role), 32)) | |||
for _, r := range aP.Roles { | |||
if r == role { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// Returns true if the role is added, and false if it already exists | |||
func (aP *AccountPermissions) AddRole(role string) bool { | |||
role = string(LeftPadBytes([]byte(role), 32)) | |||
for _, r := range aP.Roles { | |||
if r == role { | |||
return false | |||
} | |||
} | |||
aP.Roles = append(aP.Roles, role) | |||
return true | |||
} | |||
// Returns true if the role is removed, and false if it is not found | |||
func (aP *AccountPermissions) RmRole(role string) bool { | |||
role = string(LeftPadBytes([]byte(role), 32)) | |||
for i, r := range aP.Roles { | |||
if r == role { | |||
post := []string{} | |||
if len(aP.Roles) > i+1 { | |||
post = aP.Roles[i+1:] | |||
} | |||
aP.Roles = append(aP.Roles[:i], post...) | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// string utilities | |||
// PermFlagToString assumes the permFlag is valid, else returns "#-UNKNOWN-#" | |||
func PermFlagToString(pf PermFlag) (perm string) { | |||
switch pf { | |||
case Root: | |||
perm = "root" | |||
case Send: | |||
perm = "send" | |||
case Call: | |||
perm = "call" | |||
case CreateContract: | |||
perm = "create_contract" | |||
case CreateAccount: | |||
perm = "create_account" | |||
case Bond: | |||
perm = "bond" | |||
case Name: | |||
perm = "name" | |||
case HasBase: | |||
perm = "has_base" | |||
case SetBase: | |||
perm = "set_base" | |||
case UnsetBase: | |||
perm = "unset_base" | |||
case SetGlobal: | |||
perm = "set_global" | |||
case HasRole: | |||
perm = "has_role" | |||
case AddRole: | |||
perm = "add_role" | |||
case RmRole: | |||
perm = "rm_role" | |||
default: | |||
perm = "#-UNKNOWN-#" | |||
} | |||
return | |||
} | |||
func PermStringToFlag(perm string) (pf PermFlag, err error) { | |||
switch perm { | |||
case "root": | |||
pf = Root | |||
case "send": | |||
pf = Send | |||
case "call": | |||
pf = Call | |||
case "create_contract": | |||
pf = CreateContract | |||
case "create_account": | |||
pf = CreateAccount | |||
case "bond": | |||
pf = Bond | |||
case "name": | |||
pf = Name | |||
case "has_base": | |||
pf = HasBase | |||
case "set_base": | |||
pf = SetBase | |||
case "unset_base": | |||
pf = UnsetBase | |||
case "set_global": | |||
pf = SetGlobal | |||
case "has_role": | |||
pf = HasRole | |||
case "add_role": | |||
pf = AddRole | |||
case "rm_role": | |||
pf = RmRole | |||
default: | |||
err = fmt.Errorf("Unknown permission %s", perm) | |||
} | |||
return | |||
} |
@ -1,102 +0,0 @@ | |||
package types | |||
import ( | |||
"github.com/tendermint/go-wire" | |||
) | |||
//--------------------------------------------------------------------------------------------------- | |||
// PermissionsTx.PermArgs interface and argument encoding | |||
// Arguments are a registered interface in the PermissionsTx, | |||
// so binary handles the arguments and each permission function gets a type-byte | |||
// PermFlag() maps the type-byte to the permission | |||
// The account sending the PermissionsTx must have this PermFlag set | |||
type PermArgs interface { | |||
PermFlag() PermFlag | |||
} | |||
const ( | |||
PermArgsTypeHasBase = byte(0x01) | |||
PermArgsTypeSetBase = byte(0x02) | |||
PermArgsTypeUnsetBase = byte(0x03) | |||
PermArgsTypeSetGlobal = byte(0x04) | |||
PermArgsTypeHasRole = byte(0x05) | |||
PermArgsTypeAddRole = byte(0x06) | |||
PermArgsTypeRmRole = byte(0x07) | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ PermArgs }{}, | |||
wire.ConcreteType{&HasBaseArgs{}, PermArgsTypeHasBase}, | |||
wire.ConcreteType{&SetBaseArgs{}, PermArgsTypeSetBase}, | |||
wire.ConcreteType{&UnsetBaseArgs{}, PermArgsTypeUnsetBase}, | |||
wire.ConcreteType{&SetGlobalArgs{}, PermArgsTypeSetGlobal}, | |||
wire.ConcreteType{&HasRoleArgs{}, PermArgsTypeHasRole}, | |||
wire.ConcreteType{&AddRoleArgs{}, PermArgsTypeAddRole}, | |||
wire.ConcreteType{&RmRoleArgs{}, PermArgsTypeRmRole}, | |||
) | |||
type HasBaseArgs struct { | |||
Address []byte `json:"address"` | |||
Permission PermFlag `json:"permission"` | |||
} | |||
func (*HasBaseArgs) PermFlag() PermFlag { | |||
return HasBase | |||
} | |||
type SetBaseArgs struct { | |||
Address []byte `json:"address"` | |||
Permission PermFlag `json:"permission"` | |||
Value bool `json:"value"` | |||
} | |||
func (*SetBaseArgs) PermFlag() PermFlag { | |||
return SetBase | |||
} | |||
type UnsetBaseArgs struct { | |||
Address []byte `json:"address"` | |||
Permission PermFlag `json:"permission"` | |||
} | |||
func (*UnsetBaseArgs) PermFlag() PermFlag { | |||
return UnsetBase | |||
} | |||
type SetGlobalArgs struct { | |||
Permission PermFlag `json:"permission"` | |||
Value bool `json:"value"` | |||
} | |||
func (*SetGlobalArgs) PermFlag() PermFlag { | |||
return SetGlobal | |||
} | |||
type HasRoleArgs struct { | |||
Address []byte `json:"address"` | |||
Role string `json:"role"` | |||
} | |||
func (*HasRoleArgs) PermFlag() PermFlag { | |||
return HasRole | |||
} | |||
type AddRoleArgs struct { | |||
Address []byte `json:"address"` | |||
Role string `json:"role"` | |||
} | |||
func (*AddRoleArgs) PermFlag() PermFlag { | |||
return AddRole | |||
} | |||
type RmRoleArgs struct { | |||
Address []byte `json:"address"` | |||
Role string `json:"role"` | |||
} | |||
func (*RmRoleArgs) PermFlag() PermFlag { | |||
return RmRole | |||
} |
@ -1,67 +0,0 @@ | |||
package core | |||
import ( | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
) | |||
func GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { | |||
return &ctypes.ResultGenPrivAccount{acm.GenPrivAccount()}, nil | |||
} | |||
// If the account is not known, returns nil, nil. | |||
func GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { | |||
cache := mempoolReactor.Mempool.GetCache() | |||
account := cache.GetAccount(address) | |||
if account == nil { | |||
return nil, nil | |||
} | |||
return &ctypes.ResultGetAccount{account}, nil | |||
} | |||
func GetStorage(address, key []byte) (*ctypes.ResultGetStorage, error) { | |||
state := consensusState.GetState() | |||
account := state.GetAccount(address) | |||
if account == nil { | |||
return nil, fmt.Errorf("UnknownAddress: %X", address) | |||
} | |||
storageRoot := account.StorageRoot | |||
storageTree := state.LoadStorage(storageRoot) | |||
_, value := storageTree.Get(LeftPadWord256(key).Bytes()) | |||
if value == nil { | |||
return &ctypes.ResultGetStorage{key, nil}, nil | |||
} | |||
return &ctypes.ResultGetStorage{key, value.([]byte)}, nil | |||
} | |||
func ListAccounts() (*ctypes.ResultListAccounts, error) { | |||
var blockHeight int | |||
var accounts []*acm.Account | |||
state := consensusState.GetState() | |||
blockHeight = state.LastBlockHeight | |||
state.GetAccounts().Iterate(func(key interface{}, value interface{}) bool { | |||
accounts = append(accounts, value.(*acm.Account)) | |||
return false | |||
}) | |||
return &ctypes.ResultListAccounts{blockHeight, accounts}, nil | |||
} | |||
func DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { | |||
state := consensusState.GetState() | |||
account := state.GetAccount(address) | |||
if account == nil { | |||
return nil, fmt.Errorf("UnknownAddress: %X", address) | |||
} | |||
storageRoot := account.StorageRoot | |||
storageTree := state.LoadStorage(storageRoot) | |||
storageItems := []ctypes.StorageItem{} | |||
storageTree.Iterate(func(key interface{}, value interface{}) bool { | |||
storageItems = append(storageItems, ctypes.StorageItem{ | |||
key.([]byte), value.([]byte)}) | |||
return false | |||
}) | |||
return &ctypes.ResultDumpStorage{storageRoot, storageItems}, nil | |||
} |
@ -1,29 +0,0 @@ | |||
package core | |||
import ( | |||
"fmt" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
func GetName(name string) (*ctypes.ResultGetName, error) { | |||
st := consensusState.GetState() // performs a copy | |||
entry := st.GetNameRegEntry(name) | |||
if entry == nil { | |||
return nil, fmt.Errorf("Name %s not found", name) | |||
} | |||
return &ctypes.ResultGetName{entry}, nil | |||
} | |||
func ListNames() (*ctypes.ResultListNames, error) { | |||
var blockHeight int | |||
var names []*types.NameRegEntry | |||
state := consensusState.GetState() | |||
blockHeight = state.LastBlockHeight | |||
state.GetNames().Iterate(func(key interface{}, value interface{}) bool { | |||
names = append(names, value.(*types.NameRegEntry)) | |||
return false | |||
}) | |||
return &ctypes.ResultListNames{blockHeight, names}, nil | |||
} |
@ -1,116 +0,0 @@ | |||
package core | |||
import ( | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
"github.com/tendermint/tendermint/state" | |||
"github.com/tendermint/tendermint/types" | |||
"github.com/tendermint/tendermint/vm" | |||
) | |||
func toVMAccount(acc *acm.Account) *vm.Account { | |||
return &vm.Account{ | |||
Address: LeftPadWord256(acc.Address), | |||
Balance: acc.Balance, | |||
Code: acc.Code, // This is crazy. | |||
Nonce: int64(acc.Sequence), | |||
Other: acc.PubKey, | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Run a contract's code on an isolated and unpersisted state | |||
// Cannot be used to create new contracts | |||
func Call(fromAddress, toAddress, data []byte) (*ctypes.ResultCall, error) { | |||
st := consensusState.GetState() // performs a copy | |||
cache := state.NewBlockCache(st) | |||
outAcc := cache.GetAccount(toAddress) | |||
if outAcc == nil { | |||
return nil, fmt.Errorf("Account %x does not exist", toAddress) | |||
} | |||
callee := toVMAccount(outAcc) | |||
caller := &vm.Account{Address: LeftPadWord256(fromAddress)} | |||
txCache := state.NewTxCache(cache) | |||
params := vm.Params{ | |||
BlockHeight: int64(st.LastBlockHeight), | |||
BlockHash: LeftPadWord256(st.LastBlockHash), | |||
BlockTime: st.LastBlockTime.Unix(), | |||
GasLimit: st.GetGasLimit(), | |||
} | |||
vmach := vm.NewVM(txCache, params, caller.Address, nil) | |||
gas := st.GetGasLimit() | |||
ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &ctypes.ResultCall{Return: ret}, nil | |||
} | |||
// Run the given code on an isolated and unpersisted state | |||
// Cannot be used to create new contracts | |||
func CallCode(fromAddress, code, data []byte) (*ctypes.ResultCall, error) { | |||
st := consensusState.GetState() // performs a copy | |||
cache := mempoolReactor.Mempool.GetCache() | |||
callee := &vm.Account{Address: LeftPadWord256(fromAddress)} | |||
caller := &vm.Account{Address: LeftPadWord256(fromAddress)} | |||
txCache := state.NewTxCache(cache) | |||
params := vm.Params{ | |||
BlockHeight: int64(st.LastBlockHeight), | |||
BlockHash: LeftPadWord256(st.LastBlockHash), | |||
BlockTime: st.LastBlockTime.Unix(), | |||
GasLimit: st.GetGasLimit(), | |||
} | |||
vmach := vm.NewVM(txCache, params, caller.Address, nil) | |||
gas := st.GetGasLimit() | |||
ret, err := vmach.Call(caller, callee, code, data, 0, &gas) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &ctypes.ResultCall{Return: ret}, nil | |||
} | |||
//----------------------------------------------------------------------------- | |||
func SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { | |||
// more checks? | |||
for i, privAccount := range privAccounts { | |||
if privAccount == nil || privAccount.PrivKey == nil { | |||
return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i) | |||
} | |||
} | |||
switch tx.(type) { | |||
case *types.SendTx: | |||
sendTx := tx.(*types.SendTx) | |||
for i, input := range sendTx.Inputs { | |||
input.PubKey = privAccounts[i].PubKey | |||
input.Signature = privAccounts[i].Sign(config.GetString("chain_id"), sendTx) | |||
} | |||
case *types.CallTx: | |||
callTx := tx.(*types.CallTx) | |||
callTx.Input.PubKey = privAccounts[0].PubKey | |||
callTx.Input.Signature = privAccounts[0].Sign(config.GetString("chain_id"), callTx) | |||
case *types.BondTx: | |||
bondTx := tx.(*types.BondTx) | |||
// the first privaccount corresponds to the BondTx pub key. | |||
// the rest to the inputs | |||
bondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), bondTx).(acm.SignatureEd25519) | |||
for i, input := range bondTx.Inputs { | |||
input.PubKey = privAccounts[i+1].PubKey | |||
input.Signature = privAccounts[i+1].Sign(config.GetString("chain_id"), bondTx) | |||
} | |||
case *types.UnbondTx: | |||
unbondTx := tx.(*types.UnbondTx) | |||
unbondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), unbondTx).(acm.SignatureEd25519) | |||
case *types.RebondTx: | |||
rebondTx := tx.(*types.RebondTx) | |||
rebondTx.Signature = privAccounts[0].Sign(config.GetString("chain_id"), rebondTx).(acm.SignatureEd25519) | |||
} | |||
return &ctypes.ResultSignTx{tx}, nil | |||
} |
@ -1,236 +0,0 @@ | |||
package core_client | |||
import ( | |||
"bytes" | |||
"fmt" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
rpctypes "github.com/tendermint/tendermint/rpc/types" | |||
"github.com/tendermint/go-wire" | |||
"io/ioutil" | |||
"net/http" | |||
"net/url" | |||
//"reflect" | |||
// Uncomment to use go:generate | |||
// _ "github.com/tendermint/go-rpc-gen" | |||
) | |||
// maps camel-case function names to lower case rpc version | |||
var reverseFuncMap = map[string]string{ | |||
"Status": "status", | |||
"NetInfo": "net_info", | |||
"BlockchainInfo": "blockchain", | |||
"Genesis": "genesis", | |||
"GetBlock": "get_block", | |||
"GetAccount": "get_account", | |||
"GetStorage": "get_storage", | |||
"Call": "call", | |||
"CallCode": "call_code", | |||
"ListValidators": "list_validators", | |||
"DumpConsensusState": "dump_consensus_state", | |||
"DumpStorage": "dump_storage", | |||
"BroadcastTx": "broadcast_tx", | |||
"ListUnconfirmedTxs": "list_unconfirmed_txs", | |||
"ListAccounts": "list_accounts", | |||
"GetName": "get_name", | |||
"ListNames": "list_names", | |||
"GenPrivAccount": "unsafe/gen_priv_account", | |||
"SignTx": "unsafe/sign_tx", | |||
} | |||
/* | |||
// fill the map from camelcase to lowercase | |||
func fillReverseFuncMap() map[string]string { | |||
fMap := make(map[string]string) | |||
for name, f := range core.Routes { | |||
camelName := runtime.FuncForPC(f.f.Pointer()).Name() | |||
spl := strings.Split(camelName, ".") | |||
if len(spl) > 1 { | |||
camelName = spl[len(spl)-1] | |||
} | |||
fMap[camelName] = name | |||
} | |||
return fMap | |||
} | |||
*/ | |||
type Response struct { | |||
Status string | |||
Data interface{} | |||
Error string | |||
} | |||
//go:generate go-rpc-gen -interface Client -dir ../core -pkg core -type *ClientHTTP,*ClientJSON -exclude pipe.go -out-pkg core_client | |||
type ClientJSON struct { | |||
addr string | |||
} | |||
type ClientHTTP struct { | |||
addr string | |||
} | |||
func NewClient(addr, typ string) Client { | |||
switch typ { | |||
case "HTTP": | |||
return &ClientHTTP{addr} | |||
case "JSONRPC": | |||
return &ClientJSON{addr} | |||
default: | |||
panic("Unknown client type " + typ + ". Select HTTP or JSONRPC") | |||
} | |||
return nil | |||
} | |||
func argsToJson(args ...interface{}) ([]string, error) { | |||
l := len(args) | |||
jsons := make([]string, l) | |||
n, err := new(int64), new(error) | |||
for i, a := range args { | |||
buf := new(bytes.Buffer) | |||
wire.WriteJSON(a, buf, n, err) | |||
if *err != nil { | |||
return nil, *err | |||
} | |||
jsons[i] = string(buf.Bytes()) | |||
} | |||
return jsons, nil | |||
} | |||
func (c *ClientJSON) RequestResponse(s rpctypes.RPCRequest) (b []byte, err error) { | |||
b = wire.JSONBytes(s) | |||
buf := bytes.NewBuffer(b) | |||
resp, err := http.Post(c.addr, "text/json", buf) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer resp.Body.Close() | |||
return ioutil.ReadAll(resp.Body) | |||
} | |||
/* | |||
What follows is used by `rpc-gen` when `go generate` is called | |||
to populate the rpc client methods | |||
*/ | |||
// first we define the base interface, which rpc-gen will further populate with generated methods | |||
/*rpc-gen:define-interface Client | |||
type Client interface { | |||
Address() string // returns the remote address | |||
} | |||
*/ | |||
// encoding functions | |||
func binaryWriter(args ...interface{}) ([]interface{}, error) { | |||
list := []interface{}{} | |||
for _, a := range args { | |||
buf, n, err := new(bytes.Buffer), new(int64), new(error) | |||
wire.WriteJSON(a, buf, n, err) | |||
if *err != nil { | |||
return nil, *err | |||
} | |||
list = append(list, buf.Bytes()) | |||
} | |||
return list, nil | |||
} | |||
func argsToURLValues(argNames []string, args ...interface{}) (url.Values, error) { | |||
values := make(url.Values) | |||
if len(argNames) == 0 { | |||
return values, nil | |||
} | |||
if len(argNames) != len(args) { | |||
return nil, fmt.Errorf("argNames and args have different lengths: %d, %d", len(argNames), len(args)) | |||
} | |||
slice, err := argsToJson(args...) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for i, name := range argNames { | |||
s := slice[i] | |||
values.Set(name, s) // s[0] | |||
/*for j := 1; j < len(s); j++ { | |||
values.Add(name, s[j]) | |||
}*/ | |||
} | |||
return values, nil | |||
} | |||
func unmarshalCheckResponse(body []byte) (response *ctypes.Response, err error) { | |||
response = new(ctypes.Response) | |||
wire.ReadJSON(response, body, &err) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if response.Error != "" { | |||
return nil, fmt.Errorf(response.Error) | |||
} | |||
return response, nil | |||
} | |||
// import statements we will need for the templates | |||
/*rpc-gen:imports: | |||
rpctypes github.com/tendermint/tendermint/rpc/types | |||
net/http | |||
io/ioutil | |||
fmt | |||
*/ | |||
// Template functions to be filled in | |||
/*rpc-gen:template:*ClientJSON func (c *ClientJSON) {{name}}({{args.def}}) ({{response}}) { | |||
request := rpctypes.RPCRequest{ | |||
JSONRPC: "2.0", | |||
Method: reverseFuncMap["{{name}}"], | |||
Params: []interface{}{ {{args.ident}} }, | |||
ID: "", | |||
} | |||
body, err := c.RequestResponse(request) | |||
if err != nil{ | |||
return nil, err | |||
} | |||
response, err := unmarshalCheckResponse(body) | |||
if err != nil{ | |||
return nil, err | |||
} | |||
if response.Result == nil { | |||
return nil, nil | |||
} | |||
result, ok := response.Result.({{response.0}}) | |||
if !ok{ | |||
return nil, fmt.Errorf("response result was wrong type") | |||
} | |||
return result, nil | |||
}*/ | |||
/*rpc-gen:template:*ClientHTTP func (c *ClientHTTP) {{name}}({{args.def}}) ({{response}}){ | |||
values, err := argsToURLValues({{args.name}}, {{args.ident}}) | |||
if err != nil{ | |||
return nil, err | |||
} | |||
resp, err := http.PostForm(c.addr+reverseFuncMap["{{name}}"], values) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer resp.Body.Close() | |||
body, err := ioutil.ReadAll(resp.Body) | |||
if err != nil { | |||
return nil, err | |||
} | |||
response, err := unmarshalCheckResponse(body) | |||
if err != nil{ | |||
return nil, err | |||
} | |||
if response.Result == nil { | |||
return nil, nil | |||
} | |||
result, ok := response.Result.({{response.0}}) | |||
if !ok{ | |||
return nil, fmt.Errorf("response result was wrong type") | |||
} | |||
return result, nil | |||
}*/ |
@ -1,8 +0,0 @@ | |||
package core_client | |||
import ( | |||
"github.com/tendermint/log15" | |||
) | |||
var log = log15.New("module", "core_client") |
@ -1,119 +0,0 @@ | |||
package core_client | |||
import ( | |||
"net/http" | |||
"strings" | |||
"time" | |||
"github.com/gorilla/websocket" | |||
. "github.com/tendermint/go-common" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
"github.com/tendermint/tendermint/rpc/types" | |||
"github.com/tendermint/go-wire" | |||
) | |||
const ( | |||
wsEventsChannelCapacity = 10 | |||
wsResultsChannelCapacity = 10 | |||
wsWriteTimeoutSeconds = 10 | |||
) | |||
type WSClient struct { | |||
QuitService | |||
Address string | |||
*websocket.Conn | |||
EventsCh chan ctypes.ResultEvent | |||
ResultsCh chan ctypes.Result | |||
} | |||
// create a new connection | |||
func NewWSClient(addr string) *WSClient { | |||
wsClient := &WSClient{ | |||
Address: addr, | |||
Conn: nil, | |||
EventsCh: make(chan ctypes.ResultEvent, wsEventsChannelCapacity), | |||
ResultsCh: make(chan ctypes.Result, wsResultsChannelCapacity), | |||
} | |||
wsClient.QuitService = *NewQuitService(log, "WSClient", wsClient) | |||
return wsClient | |||
} | |||
func (wsc *WSClient) OnStart() error { | |||
wsc.QuitService.OnStart() | |||
err := wsc.dial() | |||
if err != nil { | |||
return err | |||
} | |||
go wsc.receiveEventsRoutine() | |||
return nil | |||
} | |||
func (wsc *WSClient) dial() error { | |||
// Dial | |||
dialer := websocket.DefaultDialer | |||
rHeader := http.Header{} | |||
con, _, err := dialer.Dial(wsc.Address, rHeader) | |||
if err != nil { | |||
return err | |||
} | |||
// Set the ping/pong handlers | |||
con.SetPingHandler(func(m string) error { | |||
con.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds)) | |||
return nil | |||
}) | |||
con.SetPongHandler(func(m string) error { | |||
return nil | |||
}) | |||
wsc.Conn = con | |||
return nil | |||
} | |||
func (wsc *WSClient) OnStop() { | |||
wsc.QuitService.OnStop() | |||
} | |||
func (wsc *WSClient) receiveEventsRoutine() { | |||
for { | |||
_, data, err := wsc.ReadMessage() | |||
if err != nil { | |||
log.Info("WSClient failed to read message", "error", err, "data", string(data)) | |||
wsc.Stop() | |||
break | |||
} else { | |||
var response ctypes.Response | |||
wire.ReadJSON(&response, data, &err) | |||
if err != nil { | |||
log.Info("WSClient failed to parse message", "error", err) | |||
wsc.Stop() | |||
break | |||
} | |||
if strings.HasSuffix(response.ID, "#event") { | |||
wsc.EventsCh <- *response.Result.(*ctypes.ResultEvent) | |||
} else { | |||
wsc.ResultsCh <- response.Result | |||
} | |||
} | |||
} | |||
} | |||
// subscribe to an event | |||
func (wsc *WSClient) Subscribe(eventid string) error { | |||
err := wsc.WriteJSON(rpctypes.RPCRequest{ | |||
JSONRPC: "2.0", | |||
ID: "", | |||
Method: "subscribe", | |||
Params: []interface{}{eventid}, | |||
}) | |||
return err | |||
} | |||
// unsubscribe from an event | |||
func (wsc *WSClient) Unsubscribe(eventid string) error { | |||
err := wsc.WriteJSON(rpctypes.RPCRequest{ | |||
JSONRPC: "2.0", | |||
ID: "", | |||
Method: "unsubscribe", | |||
Params: []interface{}{eventid}, | |||
}) | |||
return err | |||
} |
@ -1,129 +0,0 @@ | |||
package rpctest | |||
import ( | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
"testing" | |||
) | |||
// When run with `-test.short` we only run: | |||
// TestHTTPStatus, TestHTTPBroadcast, TestJSONStatus, TestJSONBroadcast, TestWSConnect, TestWSSend | |||
//-------------------------------------------------------------------------------- | |||
// Test the HTTP client | |||
func TestHTTPStatus(t *testing.T) { | |||
testStatus(t, "HTTP") | |||
} | |||
func TestHTTPGenPriv(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGenPriv(t, "HTTP") | |||
} | |||
func TestHTTPGetAccount(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGetAccount(t, "HTTP") | |||
} | |||
func TestHTTPSignedTx(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testSignedTx(t, "HTTP") | |||
} | |||
func TestHTTPBroadcastTx(t *testing.T) { | |||
testBroadcastTx(t, "HTTP") | |||
} | |||
func TestHTTPGetStorage(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGetStorage(t, "HTTP") | |||
} | |||
func TestHTTPCallCode(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testCallCode(t, "HTTP") | |||
} | |||
func TestHTTPCallContract(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testCall(t, "HTTP") | |||
} | |||
func TestHTTPNameReg(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testNameReg(t, "HTTP") | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// Test the JSONRPC client | |||
func TestJSONStatus(t *testing.T) { | |||
testStatus(t, "JSONRPC") | |||
} | |||
func TestJSONGenPriv(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGenPriv(t, "JSONRPC") | |||
} | |||
func TestJSONGetAccount(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGetAccount(t, "JSONRPC") | |||
} | |||
func TestJSONSignedTx(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testSignedTx(t, "JSONRPC") | |||
} | |||
func TestJSONBroadcastTx(t *testing.T) { | |||
testBroadcastTx(t, "JSONRPC") | |||
} | |||
func TestJSONGetStorage(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testGetStorage(t, "JSONRPC") | |||
} | |||
func TestJSONCallCode(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testCallCode(t, "JSONRPC") | |||
} | |||
func TestJSONCallContract(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testCall(t, "JSONRPC") | |||
} | |||
func TestJSONNameReg(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
testNameReg(t, "JSONRPC") | |||
} |
@ -1,212 +0,0 @@ | |||
package rpctest | |||
import ( | |||
"fmt" | |||
"testing" | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
var wsTyp = "JSONRPC" | |||
//-------------------------------------------------------------------------------- | |||
// Test the websocket service | |||
// make a simple connection to the server | |||
func TestWSConnect(t *testing.T) { | |||
con := newWSCon(t) | |||
con.Close() | |||
} | |||
// receive a new block message | |||
func TestWSNewBlock(t *testing.T) { | |||
con := newWSCon(t) | |||
eid := types.EventStringNewBlock() | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error { | |||
fmt.Println("Check:", string(b)) | |||
return nil | |||
}) | |||
} | |||
// receive a few new block messages in a row, with increasing height | |||
func TestWSBlockchainGrowth(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
con := newWSCon(t) | |||
eid := types.EventStringNewBlock() | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
// listen for NewBlock, ensure height increases by 1 | |||
unmarshalValidateBlockchain(t, con, eid) | |||
} | |||
// send a transaction and validate the events from listening for both sender and receiver | |||
func TestWSSend(t *testing.T) { | |||
toAddr := user[1].Address | |||
amt := int64(100) | |||
con := newWSCon(t) | |||
eidInput := types.EventStringAccInput(user[0].Address) | |||
eidOutput := types.EventStringAccOutput(toAddr) | |||
subscribe(t, con, eidInput) | |||
subscribe(t, con, eidOutput) | |||
defer func() { | |||
unsubscribe(t, con, eidInput) | |||
unsubscribe(t, con, eidOutput) | |||
con.Close() | |||
}() | |||
waitForEvent(t, con, eidInput, true, func() { | |||
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt) | |||
broadcastTx(t, wsTyp, tx) | |||
}, unmarshalValidateSend(amt, toAddr)) | |||
waitForEvent(t, con, eidOutput, true, func() {}, unmarshalValidateSend(amt, toAddr)) | |||
} | |||
// ensure events are only fired once for a given transaction | |||
func TestWSDoubleFire(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
con := newWSCon(t) | |||
eid := types.EventStringAccInput(user[0].Address) | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
amt := int64(100) | |||
toAddr := user[1].Address | |||
// broadcast the transaction, wait to hear about it | |||
waitForEvent(t, con, eid, true, func() { | |||
tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt) | |||
broadcastTx(t, wsTyp, tx) | |||
}, func(eid string, b []byte) error { | |||
return nil | |||
}) | |||
// but make sure we don't hear about it twice | |||
waitForEvent(t, con, eid, false, func() { | |||
}, func(eid string, b []byte) error { | |||
return nil | |||
}) | |||
} | |||
// create a contract, wait for the event, and send it a msg, validate the return | |||
func TestWSCallWait(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
con := newWSCon(t) | |||
eid1 := types.EventStringAccInput(user[0].Address) | |||
subscribe(t, con, eid1) | |||
defer func() { | |||
unsubscribe(t, con, eid1) | |||
con.Close() | |||
}() | |||
amt, gasLim, fee := int64(10000), int64(1000), int64(1000) | |||
code, returnCode, returnVal := simpleContract() | |||
var contractAddr []byte | |||
// wait for the contract to be created | |||
waitForEvent(t, con, eid1, true, func() { | |||
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee) | |||
receipt := broadcastTx(t, wsTyp, tx) | |||
contractAddr = receipt.ContractAddr | |||
}, unmarshalValidateTx(amt, returnCode)) | |||
// susbscribe to the new contract | |||
amt = int64(10001) | |||
eid2 := types.EventStringAccOutput(contractAddr) | |||
subscribe(t, con, eid2) | |||
defer func() { | |||
unsubscribe(t, con, eid2) | |||
}() | |||
// get the return value from a call | |||
data := []byte{0x1} | |||
waitForEvent(t, con, eid2, true, func() { | |||
tx := makeDefaultCallTx(t, wsTyp, contractAddr, data, amt, gasLim, fee) | |||
receipt := broadcastTx(t, wsTyp, tx) | |||
contractAddr = receipt.ContractAddr | |||
}, unmarshalValidateTx(amt, returnVal)) | |||
} | |||
// create a contract and send it a msg without waiting. wait for contract event | |||
// and validate return | |||
func TestWSCallNoWait(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
con := newWSCon(t) | |||
amt, gasLim, fee := int64(10000), int64(1000), int64(1000) | |||
code, _, returnVal := simpleContract() | |||
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee) | |||
receipt := broadcastTx(t, wsTyp, tx) | |||
contractAddr := receipt.ContractAddr | |||
// susbscribe to the new contract | |||
amt = int64(10001) | |||
eid := types.EventStringAccOutput(contractAddr) | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
// get the return value from a call | |||
data := []byte{0x1} | |||
waitForEvent(t, con, eid, true, func() { | |||
tx := makeDefaultCallTx(t, wsTyp, contractAddr, data, amt, gasLim, fee) | |||
broadcastTx(t, wsTyp, tx) | |||
}, unmarshalValidateTx(amt, returnVal)) | |||
} | |||
// create two contracts, one of which calls the other | |||
func TestWSCallCall(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("skipping test in short mode.") | |||
} | |||
con := newWSCon(t) | |||
amt, gasLim, fee := int64(10000), int64(1000), int64(1000) | |||
code, _, returnVal := simpleContract() | |||
txid := new([]byte) | |||
// deploy the two contracts | |||
tx := makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee) | |||
receipt := broadcastTx(t, wsTyp, tx) | |||
contractAddr1 := receipt.ContractAddr | |||
code, _, _ = simpleCallContract(contractAddr1) | |||
tx = makeDefaultCallTx(t, wsTyp, nil, code, amt, gasLim, fee) | |||
receipt = broadcastTx(t, wsTyp, tx) | |||
contractAddr2 := receipt.ContractAddr | |||
// susbscribe to the new contracts | |||
amt = int64(10001) | |||
eid1 := types.EventStringAccCall(contractAddr1) | |||
subscribe(t, con, eid1) | |||
defer func() { | |||
unsubscribe(t, con, eid1) | |||
con.Close() | |||
}() | |||
// call contract2, which should call contract1, and wait for ev1 | |||
// let the contract get created first | |||
waitForEvent(t, con, eid1, true, func() { | |||
}, func(eid string, b []byte) error { | |||
return nil | |||
}) | |||
// call it | |||
waitForEvent(t, con, eid1, true, func() { | |||
tx := makeDefaultCallTx(t, wsTyp, contractAddr2, nil, amt, gasLim, fee) | |||
broadcastTx(t, wsTyp, tx) | |||
*txid = types.TxID(chainID, tx) | |||
}, unmarshalValidateCall(user[0].Address, returnVal, txid)) | |||
} |
@ -1,13 +0,0 @@ | |||
package rpctest | |||
import ( | |||
cfg "github.com/tendermint/go-config" | |||
) | |||
var config cfg.Config = nil | |||
func init() { | |||
cfg.OnConfig(func(newConfig cfg.Config) { | |||
config = newConfig | |||
}) | |||
} |
@ -1,282 +0,0 @@ | |||
package rpctest | |||
import ( | |||
"bytes" | |||
"strconv" | |||
"testing" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
nm "github.com/tendermint/tendermint/node" | |||
"github.com/tendermint/go-p2p" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
cclient "github.com/tendermint/tendermint/rpc/core_client" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
// global variables for use across all tests | |||
var ( | |||
rpcAddr = "127.0.0.1:36657" // Not 46657 | |||
requestAddr = "http://" + rpcAddr + "/" | |||
websocketAddr = "ws://" + rpcAddr + "/websocket" | |||
node *nm.Node | |||
mempoolCount = 0 | |||
// make keys | |||
user = makeUsers(5) | |||
chainID string | |||
clients = map[string]cclient.Client{ | |||
"JSONRPC": cclient.NewClient(requestAddr, "JSONRPC"), | |||
"HTTP": cclient.NewClient(requestAddr, "HTTP"), | |||
} | |||
) | |||
// deterministic account generation, synced with genesis file in config/tendermint_test/config.go | |||
func makeUsers(n int) []*acm.PrivAccount { | |||
accounts := []*acm.PrivAccount{} | |||
for i := 0; i < n; i++ { | |||
secret := ("mysecret" + strconv.Itoa(i)) | |||
user := acm.GenPrivAccountFromSecret(secret) | |||
accounts = append(accounts, user) | |||
} | |||
return accounts | |||
} | |||
// create a new node and sleep forever | |||
func newNode(ready chan struct{}) { | |||
// Create & start node | |||
node = nm.NewNode() | |||
l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr")) | |||
node.AddListener(l) | |||
node.Start() | |||
// Run the RPC server. | |||
node.StartRPC() | |||
ready <- struct{}{} | |||
// Sleep forever | |||
ch := make(chan struct{}) | |||
<-ch | |||
} | |||
// initialize config and create new node | |||
func init() { | |||
chainID = config.GetString("chain_id") | |||
// Save new priv_validator file. | |||
priv := &types.PrivValidator{ | |||
Address: user[0].Address, | |||
PubKey: acm.PubKeyEd25519(user[0].PubKey.(acm.PubKeyEd25519)), | |||
PrivKey: acm.PrivKeyEd25519(user[0].PrivKey.(acm.PrivKeyEd25519)), | |||
} | |||
priv.SetFile(config.GetString("priv_validator_file")) | |||
priv.Save() | |||
// TODO: change consensus/state.go timeouts to be shorter | |||
// start a node | |||
ready := make(chan struct{}) | |||
go newNode(ready) | |||
<-ready | |||
} | |||
//------------------------------------------------------------------------------- | |||
// some default transaction functions | |||
func makeDefaultSendTx(t *testing.T, typ string, addr []byte, amt int64) *types.SendTx { | |||
nonce := getNonce(t, typ, user[0].Address) | |||
tx := types.NewSendTx() | |||
tx.AddInputWithNonce(user[0].PubKey, amt, nonce+1) | |||
tx.AddOutput(addr, amt) | |||
return tx | |||
} | |||
func makeDefaultSendTxSigned(t *testing.T, typ string, addr []byte, amt int64) *types.SendTx { | |||
tx := makeDefaultSendTx(t, typ, addr, amt) | |||
tx.SignInput(chainID, 0, user[0]) | |||
return tx | |||
} | |||
func makeDefaultCallTx(t *testing.T, typ string, addr, code []byte, amt, gasLim, fee int64) *types.CallTx { | |||
nonce := getNonce(t, typ, user[0].Address) | |||
tx := types.NewCallTxWithNonce(user[0].PubKey, addr, code, amt, gasLim, fee, nonce+1) | |||
tx.Sign(chainID, user[0]) | |||
return tx | |||
} | |||
func makeDefaultNameTx(t *testing.T, typ string, name, value string, amt, fee int64) *types.NameTx { | |||
nonce := getNonce(t, typ, user[0].Address) | |||
tx := types.NewNameTxWithNonce(user[0].PubKey, name, value, amt, fee, nonce+1) | |||
tx.Sign(chainID, user[0]) | |||
return tx | |||
} | |||
//------------------------------------------------------------------------------- | |||
// rpc call wrappers (fail on err) | |||
// get an account's nonce | |||
func getNonce(t *testing.T, typ string, addr []byte) int { | |||
client := clients[typ] | |||
ac, err := client.GetAccount(addr) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
if ac.Account == nil { | |||
return 0 | |||
} | |||
return ac.Account.Sequence | |||
} | |||
// get the account | |||
func getAccount(t *testing.T, typ string, addr []byte) *acm.Account { | |||
client := clients[typ] | |||
ac, err := client.GetAccount(addr) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return ac.Account | |||
} | |||
// sign transaction | |||
func signTx(t *testing.T, typ string, tx types.Tx, privAcc *acm.PrivAccount) types.Tx { | |||
client := clients[typ] | |||
signedTx, err := client.SignTx(tx, []*acm.PrivAccount{privAcc}) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return signedTx.Tx | |||
} | |||
// broadcast transaction | |||
func broadcastTx(t *testing.T, typ string, tx types.Tx) ctypes.Receipt { | |||
client := clients[typ] | |||
rec, err := client.BroadcastTx(tx) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
mempoolCount += 1 | |||
return rec.Receipt | |||
} | |||
// dump all storage for an account. currently unused | |||
func dumpStorage(t *testing.T, addr []byte) ctypes.ResultDumpStorage { | |||
client := clients["HTTP"] | |||
resp, err := client.DumpStorage(addr) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return *resp | |||
} | |||
func getStorage(t *testing.T, typ string, addr, key []byte) []byte { | |||
client := clients[typ] | |||
resp, err := client.GetStorage(addr, key) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return resp.Value | |||
} | |||
func callCode(t *testing.T, client cclient.Client, fromAddress, code, data, expected []byte) { | |||
resp, err := client.CallCode(fromAddress, code, data) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
ret := resp.Return | |||
// NOTE: we don't flip memory when it comes out of RETURN (?!) | |||
if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 { | |||
t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) | |||
} | |||
} | |||
func callContract(t *testing.T, client cclient.Client, fromAddress, toAddress, data, expected []byte) { | |||
resp, err := client.Call(fromAddress, toAddress, data) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
ret := resp.Return | |||
// NOTE: we don't flip memory when it comes out of RETURN (?!) | |||
if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 { | |||
t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) | |||
} | |||
} | |||
// get the namereg entry | |||
func getNameRegEntry(t *testing.T, typ string, name string) *types.NameRegEntry { | |||
client := clients[typ] | |||
entry, err := client.GetName(name) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return entry.Entry | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// utility verification function | |||
func checkTx(t *testing.T, fromAddr []byte, priv *acm.PrivAccount, tx *types.SendTx) { | |||
if bytes.Compare(tx.Inputs[0].Address, fromAddr) != 0 { | |||
t.Fatal("Tx input addresses don't match!") | |||
} | |||
signBytes := acm.SignBytes(chainID, tx) | |||
in := tx.Inputs[0] //(*types.SendTx).Inputs[0] | |||
if err := in.ValidateBasic(); err != nil { | |||
t.Fatal(err) | |||
} | |||
// Check signatures | |||
// acc := getAccount(t, byteAddr) | |||
// NOTE: using the acc here instead of the in fails; it is nil. | |||
if !in.PubKey.VerifyBytes(signBytes, in.Signature) { | |||
t.Fatal(types.ErrTxInvalidSignature) | |||
} | |||
} | |||
// simple contract returns 5 + 6 = 0xb | |||
func simpleContract() ([]byte, []byte, []byte) { | |||
// this is the code we want to run when the contract is called | |||
contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} | |||
// the is the code we need to return the contractCode when the contract is initialized | |||
lenCode := len(contractCode) | |||
// push code to the stack | |||
//code := append([]byte{byte(0x60 + lenCode - 1)}, RightPadWord256(contractCode).Bytes()...) | |||
code := append([]byte{0x7f}, RightPadWord256(contractCode).Bytes()...) | |||
// store it in memory | |||
code = append(code, []byte{0x60, 0x0, 0x52}...) | |||
// return whats in memory | |||
//code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...) | |||
code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) | |||
// return init code, contract code, expected return | |||
return code, contractCode, LeftPadBytes([]byte{0xb}, 32) | |||
} | |||
// simple call contract calls another contract | |||
func simpleCallContract(addr []byte) ([]byte, []byte, []byte) { | |||
gas1, gas2 := byte(0x1), byte(0x1) | |||
value := byte(0x1) | |||
inOff, inSize := byte(0x0), byte(0x0) // no call data | |||
retOff, retSize := byte(0x0), byte(0x20) | |||
// this is the code we want to run (call a contract and return) | |||
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73} | |||
contractCode = append(contractCode, addr...) | |||
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...) | |||
// the is the code we need to return; the contractCode when the contract is initialized | |||
// it should copy the code from the input into memory | |||
lenCode := len(contractCode) | |||
memOff := byte(0x0) | |||
inOff = byte(0xc) // length of code before codeContract | |||
length := byte(lenCode) | |||
code := []byte{0x60, length, 0x60, inOff, 0x60, memOff, 0x37} | |||
// return whats in memory | |||
code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) | |||
code = append(code, contractCode...) | |||
// return init code, contract code, expected return | |||
return code, contractCode, LeftPadBytes([]byte{0xb}, 32) | |||
} |
@ -1,281 +0,0 @@ | |||
package rpctest | |||
import ( | |||
"bytes" | |||
"fmt" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/types" | |||
"testing" | |||
) | |||
var doNothing = func(eid string, b []byte) error { return nil } | |||
func testStatus(t *testing.T, typ string) { | |||
client := clients[typ] | |||
resp, err := client.Status() | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
if resp.NodeInfo.ChainID != chainID { | |||
t.Fatal(fmt.Errorf("ChainID mismatch: got %s expected %s", | |||
resp.NodeInfo.ChainID, chainID)) | |||
} | |||
} | |||
func testGenPriv(t *testing.T, typ string) { | |||
client := clients[typ] | |||
privAcc, err := client.GenPrivAccount() | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
if len(privAcc.PrivAccount.Address) == 0 { | |||
t.Fatal("Failed to generate an address") | |||
} | |||
} | |||
func testGetAccount(t *testing.T, typ string) { | |||
acc := getAccount(t, typ, user[0].Address) | |||
if acc == nil { | |||
t.Fatalf("Account was nil") | |||
} | |||
if bytes.Compare(acc.Address, user[0].Address) != 0 { | |||
t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, user[0].Address) | |||
} | |||
} | |||
func testSignedTx(t *testing.T, typ string) { | |||
amt := int64(100) | |||
toAddr := user[1].Address | |||
testOneSignTx(t, typ, toAddr, amt) | |||
toAddr = user[2].Address | |||
testOneSignTx(t, typ, toAddr, amt) | |||
toAddr = user[3].Address | |||
testOneSignTx(t, typ, toAddr, amt) | |||
} | |||
func testOneSignTx(t *testing.T, typ string, addr []byte, amt int64) { | |||
tx := makeDefaultSendTx(t, typ, addr, amt) | |||
tx2 := signTx(t, typ, tx, user[0]) | |||
tx2hash := types.TxID(chainID, tx2) | |||
tx.SignInput(chainID, 0, user[0]) | |||
txhash := types.TxID(chainID, tx) | |||
if bytes.Compare(txhash, tx2hash) != 0 { | |||
t.Fatal("Got different signatures for signing via rpc vs tx_utils") | |||
} | |||
tx_ := signTx(t, typ, tx, user[0]) | |||
tx = tx_.(*types.SendTx) | |||
checkTx(t, user[0].Address, user[0], tx) | |||
} | |||
func testBroadcastTx(t *testing.T, typ string) { | |||
amt := int64(100) | |||
toAddr := user[1].Address | |||
tx := makeDefaultSendTxSigned(t, typ, toAddr, amt) | |||
receipt := broadcastTx(t, typ, tx) | |||
if receipt.CreatesContract > 0 { | |||
t.Fatal("This tx does not create a contract") | |||
} | |||
if len(receipt.TxHash) == 0 { | |||
t.Fatal("Failed to compute tx hash") | |||
} | |||
pool := node.MempoolReactor().Mempool | |||
txs := pool.GetProposalTxs() | |||
if len(txs) != mempoolCount { | |||
t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount) | |||
} | |||
tx2 := txs[mempoolCount-1].(*types.SendTx) | |||
n, err := new(int64), new(error) | |||
buf1, buf2 := new(bytes.Buffer), new(bytes.Buffer) | |||
tx.WriteSignBytes(chainID, buf1, n, err) | |||
tx2.WriteSignBytes(chainID, buf2, n, err) | |||
if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 { | |||
t.Fatal("inconsistent hashes for mempool tx and sent tx") | |||
} | |||
} | |||
func testGetStorage(t *testing.T, typ string) { | |||
con := newWSCon(t) | |||
eid := types.EventStringNewBlock() | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
amt, gasLim, fee := int64(1100), int64(1000), int64(1000) | |||
code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} | |||
tx := makeDefaultCallTx(t, typ, nil, code, amt, gasLim, fee) | |||
receipt := broadcastTx(t, typ, tx) | |||
if receipt.CreatesContract == 0 { | |||
t.Fatal("This tx creates a contract") | |||
} | |||
if len(receipt.TxHash) == 0 { | |||
t.Fatal("Failed to compute tx hash") | |||
} | |||
contractAddr := receipt.ContractAddr | |||
if len(contractAddr) == 0 { | |||
t.Fatal("Creates contract but resulting address is empty") | |||
} | |||
// allow it to get mined | |||
waitForEvent(t, con, eid, true, func() {}, doNothing) | |||
mempoolCount = 0 | |||
v := getStorage(t, typ, contractAddr, []byte{0x1}) | |||
got := LeftPadWord256(v) | |||
expected := LeftPadWord256([]byte{0x5}) | |||
if got.Compare(expected) != 0 { | |||
t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(), expected.Bytes()) | |||
} | |||
} | |||
func testCallCode(t *testing.T, typ string) { | |||
client := clients[typ] | |||
// add two integers and return the result | |||
code := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} | |||
data := []byte{} | |||
expected := []byte{0xb} | |||
callCode(t, client, user[0].PubKey.Address(), code, data, expected) | |||
// pass two ints as calldata, add, and return the result | |||
code = []byte{0x60, 0x0, 0x35, 0x60, 0x20, 0x35, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} | |||
data = append(LeftPadWord256([]byte{0x5}).Bytes(), LeftPadWord256([]byte{0x6}).Bytes()...) | |||
expected = []byte{0xb} | |||
callCode(t, client, user[0].PubKey.Address(), code, data, expected) | |||
} | |||
func testCall(t *testing.T, typ string) { | |||
con := newWSCon(t) | |||
eid := types.EventStringNewBlock() | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
client := clients[typ] | |||
// create the contract | |||
amt, gasLim, fee := int64(6969), int64(1000), int64(1000) | |||
code, _, _ := simpleContract() | |||
tx := makeDefaultCallTx(t, typ, nil, code, amt, gasLim, fee) | |||
receipt := broadcastTx(t, typ, tx) | |||
if receipt.CreatesContract == 0 { | |||
t.Fatal("This tx creates a contract") | |||
} | |||
if len(receipt.TxHash) == 0 { | |||
t.Fatal("Failed to compute tx hash") | |||
} | |||
contractAddr := receipt.ContractAddr | |||
if len(contractAddr) == 0 { | |||
t.Fatal("Creates contract but resulting address is empty") | |||
} | |||
// allow it to get mined | |||
waitForEvent(t, con, eid, true, func() {}, doNothing) | |||
mempoolCount = 0 | |||
// run a call through the contract | |||
data := []byte{} | |||
expected := []byte{0xb} | |||
callContract(t, client, user[0].PubKey.Address(), contractAddr, data, expected) | |||
} | |||
func testNameReg(t *testing.T, typ string) { | |||
client := clients[typ] | |||
con := newWSCon(t) | |||
types.MinNameRegistrationPeriod = 1 | |||
// register a new name, check if its there | |||
// since entries ought to be unique and these run against different clients, we append the typ | |||
name := "ye_old_domain_name_" + typ | |||
data := "if not now, when" | |||
fee := int64(1000) | |||
numDesiredBlocks := int64(2) | |||
amt := fee + numDesiredBlocks*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
eid := types.EventStringNameReg(name) | |||
subscribe(t, con, eid) | |||
tx := makeDefaultNameTx(t, typ, name, data, amt, fee) | |||
broadcastTx(t, typ, tx) | |||
// verify the name by both using the event and by checking get_name | |||
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error { | |||
// TODO: unmarshal the response | |||
tx, err := unmarshalResponseNameReg(b) | |||
if err != nil { | |||
return err | |||
} | |||
if tx.Name != name { | |||
t.Fatal(fmt.Sprintf("Err on received event tx.Name: Got %s, expected %s", tx.Name, name)) | |||
} | |||
if tx.Data != data { | |||
t.Fatal(fmt.Sprintf("Err on received event tx.Data: Got %s, expected %s", tx.Data, data)) | |||
} | |||
return nil | |||
}) | |||
mempoolCount = 0 | |||
entry := getNameRegEntry(t, typ, name) | |||
if entry.Data != data { | |||
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data)) | |||
} | |||
if bytes.Compare(entry.Owner, user[0].Address) != 0 { | |||
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[0].Address)) | |||
} | |||
unsubscribe(t, con, eid) | |||
// for the rest we just use new block event | |||
// since we already tested the namereg event | |||
eid = types.EventStringNewBlock() | |||
subscribe(t, con, eid) | |||
defer func() { | |||
unsubscribe(t, con, eid) | |||
con.Close() | |||
}() | |||
// update the data as the owner, make sure still there | |||
numDesiredBlocks = int64(2) | |||
data = "these are amongst the things I wish to bestow upon the youth of generations come: a safe supply of honey, and a better money. For what else shall they need" | |||
amt = fee + numDesiredBlocks*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
tx = makeDefaultNameTx(t, typ, name, data, amt, fee) | |||
broadcastTx(t, typ, tx) | |||
// commit block | |||
waitForEvent(t, con, eid, true, func() {}, doNothing) | |||
mempoolCount = 0 | |||
entry = getNameRegEntry(t, typ, name) | |||
if entry.Data != data { | |||
t.Fatal(fmt.Sprintf("Err on entry.Data: Got %s, expected %s", entry.Data, data)) | |||
} | |||
// try to update as non owner, should fail | |||
nonce := getNonce(t, typ, user[1].Address) | |||
data2 := "this is not my beautiful house" | |||
tx = types.NewNameTxWithNonce(user[1].PubKey, name, data2, amt, fee, nonce+1) | |||
tx.Sign(chainID, user[1]) | |||
_, err := client.BroadcastTx(tx) | |||
if err == nil { | |||
t.Fatal("Expected error on NameTx") | |||
} | |||
// commit block | |||
waitForEvent(t, con, eid, true, func() {}, doNothing) | |||
// now the entry should be expired, so we can update as non owner | |||
_, err = client.BroadcastTx(tx) | |||
waitForEvent(t, con, eid, true, func() {}, doNothing) | |||
mempoolCount = 0 | |||
entry = getNameRegEntry(t, typ, name) | |||
if entry.Data != data2 { | |||
t.Fatal(fmt.Sprintf("Error on entry.Data: Got %s, expected %s", entry.Data, data2)) | |||
} | |||
if bytes.Compare(entry.Owner, user[1].Address) != 0 { | |||
t.Fatal(fmt.Sprintf("Err on entry.Owner: Got %s, expected %s", entry.Owner, user[1].Address)) | |||
} | |||
} |
@ -1,271 +0,0 @@ | |||
package rpctest | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"net/http" | |||
"testing" | |||
"time" | |||
"github.com/gorilla/websocket" | |||
"github.com/tendermint/go-wire" | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||
"github.com/tendermint/tendermint/rpc/types" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
//-------------------------------------------------------------------------------- | |||
// Utilities for testing the websocket service | |||
// create a new connection | |||
func newWSCon(t *testing.T) *websocket.Conn { | |||
dialer := websocket.DefaultDialer | |||
rHeader := http.Header{} | |||
con, r, err := dialer.Dial(websocketAddr, rHeader) | |||
fmt.Println("response", r) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
return con | |||
} | |||
// subscribe to an event | |||
func subscribe(t *testing.T, con *websocket.Conn, eventid string) { | |||
err := con.WriteJSON(rpctypes.RPCRequest{ | |||
JSONRPC: "2.0", | |||
ID: "", | |||
Method: "subscribe", | |||
Params: []interface{}{eventid}, | |||
}) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
} | |||
// unsubscribe from an event | |||
func unsubscribe(t *testing.T, con *websocket.Conn, eventid string) { | |||
err := con.WriteJSON(rpctypes.RPCRequest{ | |||
JSONRPC: "2.0", | |||
ID: "", | |||
Method: "unsubscribe", | |||
Params: []interface{}{eventid}, | |||
}) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
} | |||
// wait for an event; do things that might trigger events, and check them when they are received | |||
// the check function takes an event id and the byte slice read off the ws | |||
func waitForEvent(t *testing.T, con *websocket.Conn, eventid string, dieOnTimeout bool, f func(), check func(string, []byte) error) { | |||
// go routine to wait for webscoket msg | |||
goodCh := make(chan []byte) | |||
errCh := make(chan error) | |||
quitCh := make(chan struct{}) | |||
defer close(quitCh) | |||
// Read message | |||
go func() { | |||
for { | |||
_, p, err := con.ReadMessage() | |||
if err != nil { | |||
errCh <- err | |||
break | |||
} else { | |||
// if the event id isnt what we're waiting on | |||
// ignore it | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, p, &err) | |||
if err != nil { | |||
errCh <- err | |||
break | |||
} | |||
event, ok := response.Result.(*ctypes.ResultEvent) | |||
if ok && event.Event == eventid { | |||
goodCh <- p | |||
break | |||
} | |||
} | |||
} | |||
}() | |||
// do stuff (transactions) | |||
f() | |||
// wait for an event or timeout | |||
timeout := time.NewTimer(10 * time.Second) | |||
select { | |||
case <-timeout.C: | |||
if dieOnTimeout { | |||
con.Close() | |||
t.Fatalf("%s event was not received in time", eventid) | |||
} | |||
// else that's great, we didn't hear the event | |||
// and we shouldn't have | |||
case p := <-goodCh: | |||
if dieOnTimeout { | |||
// message was received and expected | |||
// run the check | |||
err := check(eventid, p) | |||
if err != nil { | |||
t.Fatal(err) | |||
panic(err) // Show the stack trace. | |||
} | |||
} else { | |||
con.Close() | |||
t.Fatalf("%s event was not expected", eventid) | |||
} | |||
case err := <-errCh: | |||
t.Fatal(err) | |||
panic(err) // Show the stack trace. | |||
} | |||
} | |||
//-------------------------------------------------------------------------------- | |||
func unmarshalResponseNewBlock(b []byte) (*types.Block, error) { | |||
// unmarshall and assert somethings | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, b, &err) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if response.Error != "" { | |||
return nil, fmt.Errorf(response.Error) | |||
} | |||
block := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataNewBlock).Block | |||
return block, nil | |||
} | |||
func unmarshalResponseNameReg(b []byte) (*types.NameTx, error) { | |||
// unmarshall and assert somethings | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, b, &err) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if response.Error != "" { | |||
return nil, fmt.Errorf(response.Error) | |||
} | |||
tx := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx).Tx.(*types.NameTx) | |||
return tx, nil | |||
} | |||
func unmarshalValidateBlockchain(t *testing.T, con *websocket.Conn, eid string) { | |||
var initBlockN int | |||
for i := 0; i < 2; i++ { | |||
waitForEvent(t, con, eid, true, func() {}, func(eid string, b []byte) error { | |||
block, err := unmarshalResponseNewBlock(b) | |||
if err != nil { | |||
return err | |||
} | |||
if i == 0 { | |||
initBlockN = block.Header.Height | |||
} else { | |||
if block.Header.Height != initBlockN+i { | |||
return fmt.Errorf("Expected block %d, got block %d", i, block.Header.Height) | |||
} | |||
} | |||
return nil | |||
}) | |||
} | |||
} | |||
func unmarshalValidateSend(amt int64, toAddr []byte) func(string, []byte) error { | |||
return func(eid string, b []byte) error { | |||
// unmarshal and assert correctness | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, b, &err) | |||
if err != nil { | |||
return err | |||
} | |||
if response.Error != "" { | |||
return fmt.Errorf(response.Error) | |||
} | |||
if eid != response.Result.(*ctypes.ResultEvent).Event { | |||
return fmt.Errorf("Eventid is not correct. Got %s, expected %s", response.Result.(*ctypes.ResultEvent).Event, eid) | |||
} | |||
tx := response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx).Tx.(*types.SendTx) | |||
if !bytes.Equal(tx.Inputs[0].Address, user[0].Address) { | |||
return fmt.Errorf("Senders do not match up! Got %x, expected %x", tx.Inputs[0].Address, user[0].Address) | |||
} | |||
if tx.Inputs[0].Amount != amt { | |||
return fmt.Errorf("Amt does not match up! Got %d, expected %d", tx.Inputs[0].Amount, amt) | |||
} | |||
if !bytes.Equal(tx.Outputs[0].Address, toAddr) { | |||
return fmt.Errorf("Receivers do not match up! Got %x, expected %x", tx.Outputs[0].Address, user[0].Address) | |||
} | |||
return nil | |||
} | |||
} | |||
func unmarshalValidateTx(amt int64, returnCode []byte) func(string, []byte) error { | |||
return func(eid string, b []byte) error { | |||
// unmarshall and assert somethings | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, b, &err) | |||
if err != nil { | |||
return err | |||
} | |||
if response.Error != "" { | |||
return fmt.Errorf(response.Error) | |||
} | |||
var data = response.Result.(*ctypes.ResultEvent).Data.(types.EventDataTx) | |||
if data.Exception != "" { | |||
return fmt.Errorf(data.Exception) | |||
} | |||
tx := data.Tx.(*types.CallTx) | |||
if !bytes.Equal(tx.Input.Address, user[0].Address) { | |||
return fmt.Errorf("Senders do not match up! Got %x, expected %x", | |||
tx.Input.Address, user[0].Address) | |||
} | |||
if tx.Input.Amount != amt { | |||
return fmt.Errorf("Amt does not match up! Got %d, expected %d", | |||
tx.Input.Amount, amt) | |||
} | |||
ret := data.Return | |||
if !bytes.Equal(ret, returnCode) { | |||
return fmt.Errorf("Tx did not return correctly. Got %x, expected %x", ret, returnCode) | |||
} | |||
return nil | |||
} | |||
} | |||
func unmarshalValidateCall(origin, returnCode []byte, txid *[]byte) func(string, []byte) error { | |||
return func(eid string, b []byte) error { | |||
// unmarshall and assert somethings | |||
var response ctypes.Response | |||
var err error | |||
wire.ReadJSON(&response, b, &err) | |||
if err != nil { | |||
return err | |||
} | |||
if response.Error != "" { | |||
return fmt.Errorf(response.Error) | |||
} | |||
var data = response.Result.(*ctypes.ResultEvent).Data.(types.EventDataCall) | |||
if data.Exception != "" { | |||
return fmt.Errorf(data.Exception) | |||
} | |||
if !bytes.Equal(data.Origin, origin) { | |||
return fmt.Errorf("Origin does not match up! Got %x, expected %x", | |||
data.Origin, origin) | |||
} | |||
ret := data.Return | |||
if !bytes.Equal(ret, returnCode) { | |||
return fmt.Errorf("Call did not return correctly. Got %x, expected %x", ret, returnCode) | |||
} | |||
if !bytes.Equal(data.TxID, *txid) { | |||
return fmt.Errorf("TxIDs do not match up! Got %x, expected %x", | |||
data.TxID, *txid) | |||
} | |||
return nil | |||
} | |||
} |
@ -1,293 +0,0 @@ | |||
package state | |||
import ( | |||
"bytes" | |||
"sort" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
dbm "github.com/tendermint/go-db" | |||
"github.com/tendermint/go-merkle" | |||
"github.com/tendermint/tendermint/types" | |||
"github.com/tendermint/go-wire" | |||
) | |||
func makeStorage(db dbm.DB, root []byte) merkle.Tree { | |||
storage := merkle.NewIAVLTree( | |||
wire.BasicCodec, | |||
wire.BasicCodec, | |||
1024, | |||
db, | |||
) | |||
storage.Load(root) | |||
return storage | |||
} | |||
// The blockcache helps prevent unnecessary IAVLTree updates and garbage generation. | |||
type BlockCache struct { | |||
db dbm.DB | |||
backend *State | |||
accounts map[string]accountInfo | |||
storages map[Tuple256]storageInfo | |||
names map[string]nameInfo | |||
} | |||
func NewBlockCache(backend *State) *BlockCache { | |||
return &BlockCache{ | |||
db: backend.DB, | |||
backend: backend, | |||
accounts: make(map[string]accountInfo), | |||
storages: make(map[Tuple256]storageInfo), | |||
names: make(map[string]nameInfo), | |||
} | |||
} | |||
func (cache *BlockCache) State() *State { | |||
return cache.backend | |||
} | |||
//------------------------------------- | |||
// BlockCache.account | |||
func (cache *BlockCache) GetAccount(addr []byte) *acm.Account { | |||
acc, _, removed, _ := cache.accounts[string(addr)].unpack() | |||
if removed { | |||
return nil | |||
} else if acc != nil { | |||
return acc | |||
} else { | |||
acc = cache.backend.GetAccount(addr) | |||
cache.accounts[string(addr)] = accountInfo{acc, nil, false, false} | |||
return acc | |||
} | |||
} | |||
func (cache *BlockCache) UpdateAccount(acc *acm.Account) { | |||
addr := acc.Address | |||
_, storage, removed, _ := cache.accounts[string(addr)].unpack() | |||
if removed { | |||
PanicSanity("UpdateAccount on a removed account") | |||
} | |||
cache.accounts[string(addr)] = accountInfo{acc, storage, false, true} | |||
} | |||
func (cache *BlockCache) RemoveAccount(addr []byte) { | |||
_, _, removed, _ := cache.accounts[string(addr)].unpack() | |||
if removed { | |||
PanicSanity("RemoveAccount on a removed account") | |||
} | |||
cache.accounts[string(addr)] = accountInfo{nil, nil, true, false} | |||
} | |||
// BlockCache.account | |||
//------------------------------------- | |||
// BlockCache.storage | |||
func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { | |||
// Check cache | |||
info, ok := cache.storages[Tuple256{addr, key}] | |||
if ok { | |||
return info.value | |||
} | |||
// Get or load storage | |||
acc, storage, removed, dirty := cache.accounts[string(addr.Postfix(20))].unpack() | |||
if removed { | |||
PanicSanity("GetStorage() on removed account") | |||
} | |||
if acc != nil && storage == nil { | |||
storage = makeStorage(cache.db, acc.StorageRoot) | |||
cache.accounts[string(addr.Postfix(20))] = accountInfo{acc, storage, false, dirty} | |||
} else if acc == nil { | |||
return Zero256 | |||
} | |||
// Load and set cache | |||
_, val_ := storage.Get(key.Bytes()) | |||
value = Zero256 | |||
if val_ != nil { | |||
value = LeftPadWord256(val_.([]byte)) | |||
} | |||
cache.storages[Tuple256{addr, key}] = storageInfo{value, false} | |||
return value | |||
} | |||
// NOTE: Set value to zero to removed from the trie. | |||
func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) { | |||
_, _, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack() | |||
if removed { | |||
PanicSanity("SetStorage() on a removed account") | |||
} | |||
cache.storages[Tuple256{addr, key}] = storageInfo{value, true} | |||
} | |||
// BlockCache.storage | |||
//------------------------------------- | |||
// BlockCache.names | |||
func (cache *BlockCache) GetNameRegEntry(name string) *types.NameRegEntry { | |||
entry, removed, _ := cache.names[name].unpack() | |||
if removed { | |||
return nil | |||
} else if entry != nil { | |||
return entry | |||
} else { | |||
entry = cache.backend.GetNameRegEntry(name) | |||
cache.names[name] = nameInfo{entry, false, false} | |||
return entry | |||
} | |||
} | |||
func (cache *BlockCache) UpdateNameRegEntry(entry *types.NameRegEntry) { | |||
name := entry.Name | |||
cache.names[name] = nameInfo{entry, false, true} | |||
} | |||
func (cache *BlockCache) RemoveNameRegEntry(name string) { | |||
_, removed, _ := cache.names[name].unpack() | |||
if removed { | |||
PanicSanity("RemoveNameRegEntry on a removed entry") | |||
} | |||
cache.names[name] = nameInfo{nil, true, false} | |||
} | |||
// BlockCache.names | |||
//------------------------------------- | |||
// CONTRACT the updates are in deterministic order. | |||
func (cache *BlockCache) Sync() { | |||
// Determine order for storage updates | |||
// The address comes first so it'll be grouped. | |||
storageKeys := make([]Tuple256, 0, len(cache.storages)) | |||
for keyTuple := range cache.storages { | |||
storageKeys = append(storageKeys, keyTuple) | |||
} | |||
Tuple256Slice(storageKeys).Sort() | |||
// Update storage for all account/key. | |||
// Later we'll iterate over all the users and save storage + update storage root. | |||
var ( | |||
curAddr Word256 | |||
curAcc *acm.Account | |||
curAccRemoved bool | |||
curStorage merkle.Tree | |||
) | |||
for _, storageKey := range storageKeys { | |||
addr, key := Tuple256Split(storageKey) | |||
if addr != curAddr || curAcc == nil { | |||
acc, storage, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack() | |||
if !removed && storage == nil { | |||
storage = makeStorage(cache.db, acc.StorageRoot) | |||
} | |||
curAddr = addr | |||
curAcc = acc | |||
curAccRemoved = removed | |||
curStorage = storage | |||
} | |||
if curAccRemoved { | |||
continue | |||
} | |||
value, dirty := cache.storages[storageKey].unpack() | |||
if !dirty { | |||
continue | |||
} | |||
if value.IsZero() { | |||
curStorage.Remove(key.Bytes()) | |||
} else { | |||
curStorage.Set(key.Bytes(), value.Bytes()) | |||
cache.accounts[string(addr.Postfix(20))] = accountInfo{curAcc, curStorage, false, true} | |||
} | |||
} | |||
// Determine order for accounts | |||
addrStrs := []string{} | |||
for addrStr := range cache.accounts { | |||
addrStrs = append(addrStrs, addrStr) | |||
} | |||
sort.Strings(addrStrs) | |||
// Update or delete accounts. | |||
for _, addrStr := range addrStrs { | |||
acc, storage, removed, dirty := cache.accounts[addrStr].unpack() | |||
if removed { | |||
removed := cache.backend.RemoveAccount([]byte(addrStr)) | |||
if !removed { | |||
PanicCrisis(Fmt("Could not remove account to be removed: %X", acc.Address)) | |||
} | |||
} else { | |||
if acc == nil { | |||
continue | |||
} | |||
if storage != nil { | |||
newStorageRoot := storage.Save() | |||
if !bytes.Equal(newStorageRoot, acc.StorageRoot) { | |||
acc.StorageRoot = newStorageRoot | |||
dirty = true | |||
} | |||
} | |||
if dirty { | |||
cache.backend.UpdateAccount(acc) | |||
} | |||
} | |||
} | |||
// Determine order for names | |||
// note names may be of any length less than some limit | |||
nameStrs := []string{} | |||
for nameStr := range cache.names { | |||
nameStrs = append(nameStrs, nameStr) | |||
} | |||
sort.Strings(nameStrs) | |||
// Update or delete names. | |||
for _, nameStr := range nameStrs { | |||
entry, removed, dirty := cache.names[nameStr].unpack() | |||
if removed { | |||
removed := cache.backend.RemoveNameRegEntry(nameStr) | |||
if !removed { | |||
PanicCrisis(Fmt("Could not remove namereg entry to be removed: %s", nameStr)) | |||
} | |||
} else { | |||
if entry == nil { | |||
continue | |||
} | |||
if dirty { | |||
cache.backend.UpdateNameRegEntry(entry) | |||
} | |||
} | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
type accountInfo struct { | |||
account *acm.Account | |||
storage merkle.Tree | |||
removed bool | |||
dirty bool | |||
} | |||
func (accInfo accountInfo) unpack() (*acm.Account, merkle.Tree, bool, bool) { | |||
return accInfo.account, accInfo.storage, accInfo.removed, accInfo.dirty | |||
} | |||
type storageInfo struct { | |||
value Word256 | |||
dirty bool | |||
} | |||
func (stjInfo storageInfo) unpack() (Word256, bool) { | |||
return stjInfo.value, stjInfo.dirty | |||
} | |||
type nameInfo struct { | |||
name *types.NameRegEntry | |||
removed bool | |||
dirty bool | |||
} | |||
func (nInfo nameInfo) unpack() (*types.NameRegEntry, bool, bool) { | |||
return nInfo.name, nInfo.removed, nInfo.dirty | |||
} |
@ -1,18 +0,0 @@ | |||
package state | |||
import ( | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/vm" | |||
) | |||
type AccountGetter interface { | |||
GetAccount(addr []byte) *acm.Account | |||
} | |||
type VMAccountState interface { | |||
GetAccount(addr Word256) *vm.Account | |||
UpdateAccount(acc *vm.Account) | |||
RemoveAccount(acc *vm.Account) | |||
CreateAccount(creator *vm.Account) *vm.Account | |||
} |
@ -1,87 +0,0 @@ | |||
package state | |||
import ( | |||
"bytes" | |||
"encoding/hex" | |||
"fmt" | |||
"testing" | |||
tdb "github.com/tendermint/go-db" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
. "github.com/tendermint/tendermint/state/types" | |||
) | |||
var chain_id = "lone_ranger" | |||
var addr1, _ = hex.DecodeString("964B1493BBE3312278B7DEB94C39149F7899A345") | |||
var send1, name1, call1 = 1, 1, 0 | |||
var perms, setbit = 66, 70 | |||
var accName = "me" | |||
var roles1 = []string{"master", "universal-ruler"} | |||
var amt1 int64 = 1000000 | |||
var g1 = fmt.Sprintf(` | |||
{ | |||
"chain_id":"%s", | |||
"accounts": [ | |||
{ | |||
"address": "%X", | |||
"amount": %d, | |||
"name": "%s", | |||
"permissions": { | |||
"base": { | |||
"perms": %d, | |||
"set": %d | |||
}, | |||
"roles": [ | |||
"%s", | |||
"%s" | |||
] | |||
} | |||
} | |||
], | |||
"validators": [ | |||
{ | |||
"amount": 100000000, | |||
"pub_key": [1,"F6C79CF0CB9D66B677988BCB9B8EADD9A091CD465A60542A8AB85476256DBA92"], | |||
"unbond_to": [ | |||
{ | |||
"address": "964B1493BBE3312278B7DEB94C39149F7899A345", | |||
"amount": 10000000 | |||
} | |||
] | |||
} | |||
] | |||
} | |||
`, chain_id, addr1, amt1, accName, perms, setbit, roles1[0], roles1[1]) | |||
func TestGenesisReadable(t *testing.T) { | |||
genDoc := GenesisDocFromJSON([]byte(g1)) | |||
if genDoc.ChainID != chain_id { | |||
t.Fatalf("Incorrect chain id. Got %d, expected %d\n", genDoc.ChainID, chain_id) | |||
} | |||
acc := genDoc.Accounts[0] | |||
if bytes.Compare(acc.Address, addr1) != 0 { | |||
t.Fatalf("Incorrect address for account. Got %X, expected %X\n", acc.Address, addr1) | |||
} | |||
if acc.Amount != amt1 { | |||
t.Fatalf("Incorrect amount for account. Got %d, expected %d\n", acc.Amount, amt1) | |||
} | |||
if acc.Name != accName { | |||
t.Fatalf("Incorrect name for account. Got %s, expected %s\n", acc.Name, accName) | |||
} | |||
perm, _ := acc.Permissions.Base.Get(ptypes.Send) | |||
if perm != (send1 > 0) { | |||
t.Fatalf("Incorrect permission for send. Got %v, expected %v\n", perm, send1 > 0) | |||
} | |||
} | |||
func TestGenesisMakeState(t *testing.T) { | |||
genDoc := GenesisDocFromJSON([]byte(g1)) | |||
db := tdb.NewMemDB() | |||
st := MakeGenesisState(db, genDoc) | |||
acc := st.GetAccount(addr1) | |||
v, _ := acc.Permissions.Base.Get(ptypes.Send) | |||
if v != (send1 > 0) { | |||
t.Fatalf("Incorrect permission for send. Got %v, expected %v\n", v, send1 > 0) | |||
} | |||
} |
@ -1,699 +0,0 @@ | |||
package state | |||
import ( | |||
"bytes" | |||
"testing" | |||
"time" | |||
"github.com/tendermint/tendermint/account" | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
"github.com/tendermint/tendermint/types" | |||
) | |||
func execTxWithState(state *State, tx types.Tx, runCall bool) error { | |||
cache := NewBlockCache(state) | |||
if err := ExecTx(cache, tx, runCall, nil); err != nil { | |||
return err | |||
} else { | |||
cache.Sync() | |||
return nil | |||
} | |||
} | |||
func execTxWithStateNewBlock(state *State, tx types.Tx, runCall bool) error { | |||
if err := execTxWithState(state, tx, runCall); err != nil { | |||
return err | |||
} | |||
state.LastBlockHeight += 1 | |||
return nil | |||
} | |||
func TestCopyState(t *testing.T) { | |||
// Generate a random state | |||
s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000) | |||
s0Hash := s0.Hash() | |||
if len(s0Hash) == 0 { | |||
t.Error("Expected state hash") | |||
} | |||
// Check hash of copy | |||
s0Copy := s0.Copy() | |||
if !bytes.Equal(s0Hash, s0Copy.Hash()) { | |||
t.Error("Expected state copy hash to be the same") | |||
} | |||
// Mutate the original; hash should change. | |||
acc0Address := privAccounts[0].PubKey.Address() | |||
acc := s0.GetAccount(acc0Address) | |||
acc.Balance += 1 | |||
// The account balance shouldn't have changed yet. | |||
if s0.GetAccount(acc0Address).Balance == acc.Balance { | |||
t.Error("Account balance changed unexpectedly") | |||
} | |||
// Setting, however, should change the balance. | |||
s0.UpdateAccount(acc) | |||
if s0.GetAccount(acc0Address).Balance != acc.Balance { | |||
t.Error("Account balance wasn't set") | |||
} | |||
// Now that the state changed, the hash should change too. | |||
if bytes.Equal(s0Hash, s0.Hash()) { | |||
t.Error("Expected state hash to have changed") | |||
} | |||
// The s0Copy shouldn't have changed though. | |||
if !bytes.Equal(s0Hash, s0Copy.Hash()) { | |||
t.Error("Expected state copy hash to have not changed") | |||
} | |||
} | |||
func makeBlock(t *testing.T, state *State, validation *types.Validation, txs []types.Tx) *types.Block { | |||
if validation == nil { | |||
validation = &types.Validation{} | |||
} | |||
block := &types.Block{ | |||
Header: &types.Header{ | |||
ChainID: state.ChainID, | |||
Height: state.LastBlockHeight + 1, | |||
Time: state.LastBlockTime.Add(time.Minute), | |||
Fees: 0, | |||
NumTxs: len(txs), | |||
LastBlockHash: state.LastBlockHash, | |||
LastBlockParts: state.LastBlockParts, | |||
StateHash: nil, | |||
}, | |||
LastValidation: validation, | |||
Data: &types.Data{ | |||
Txs: txs, | |||
}, | |||
} | |||
block.FillHeader() | |||
// Fill in block StateHash | |||
err := state.ComputeBlockStateHash(block) | |||
if err != nil { | |||
t.Error("Error appending initial block:", err) | |||
} | |||
if len(block.Header.StateHash) == 0 { | |||
t.Error("Expected StateHash but got nothing.") | |||
} | |||
return block | |||
} | |||
func TestGenesisSaveLoad(t *testing.T) { | |||
// Generate a state, save & load it. | |||
s0, _, _ := RandGenesisState(10, true, 1000, 5, true, 1000) | |||
// Make complete block and blockParts | |||
block := makeBlock(t, s0, nil, nil) | |||
blockParts := block.MakePartSet() | |||
// Now append the block to s0. | |||
err := ExecBlock(s0, block, blockParts.Header()) | |||
if err != nil { | |||
t.Error("Error appending initial block:", err) | |||
} | |||
// Save s0 | |||
s0.Save() | |||
// Sanity check s0 | |||
//s0.DB.(*dbm.MemDB).Print() | |||
if s0.BondedValidators.TotalVotingPower() == 0 { | |||
t.Error("s0 BondedValidators TotalVotingPower should not be 0") | |||
} | |||
if s0.LastBlockHeight != 1 { | |||
t.Error("s0 LastBlockHeight should be 1, got", s0.LastBlockHeight) | |||
} | |||
// Load s1 | |||
s1 := LoadState(s0.DB) | |||
// Compare height & blockHash | |||
if s0.LastBlockHeight != s1.LastBlockHeight { | |||
t.Error("LastBlockHeight mismatch") | |||
} | |||
if !bytes.Equal(s0.LastBlockHash, s1.LastBlockHash) { | |||
t.Error("LastBlockHash mismatch") | |||
} | |||
// Compare state merkle trees | |||
if s0.BondedValidators.Size() != s1.BondedValidators.Size() { | |||
t.Error("BondedValidators Size mismatch") | |||
} | |||
if s0.BondedValidators.TotalVotingPower() != s1.BondedValidators.TotalVotingPower() { | |||
t.Error("BondedValidators TotalVotingPower mismatch") | |||
} | |||
if !bytes.Equal(s0.BondedValidators.Hash(), s1.BondedValidators.Hash()) { | |||
t.Error("BondedValidators hash mismatch") | |||
} | |||
if s0.UnbondingValidators.Size() != s1.UnbondingValidators.Size() { | |||
t.Error("UnbondingValidators Size mismatch") | |||
} | |||
if s0.UnbondingValidators.TotalVotingPower() != s1.UnbondingValidators.TotalVotingPower() { | |||
t.Error("UnbondingValidators TotalVotingPower mismatch") | |||
} | |||
if !bytes.Equal(s0.UnbondingValidators.Hash(), s1.UnbondingValidators.Hash()) { | |||
t.Error("UnbondingValidators hash mismatch") | |||
} | |||
if !bytes.Equal(s0.accounts.Hash(), s1.accounts.Hash()) { | |||
t.Error("Accounts mismatch") | |||
} | |||
if !bytes.Equal(s0.validatorInfos.Hash(), s1.validatorInfos.Hash()) { | |||
t.Error("Accounts mismatch") | |||
} | |||
} | |||
func TestTxSequence(t *testing.T) { | |||
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) | |||
acc0 := state.GetAccount(privAccounts[0].PubKey.Address()) | |||
acc0PubKey := privAccounts[0].PubKey | |||
acc1 := state.GetAccount(privAccounts[1].PubKey.Address()) | |||
// Test a variety of sequence numbers for the tx. | |||
// The tx should only pass when i == 1. | |||
for i := -1; i < 3; i++ { | |||
sequence := acc0.Sequence + i | |||
tx := types.NewSendTx() | |||
tx.AddInputWithNonce(acc0PubKey, 1, sequence) | |||
tx.AddOutput(acc1.Address, 1) | |||
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
stateCopy := state.Copy() | |||
err := execTxWithState(stateCopy, tx, true) | |||
if i == 1 { | |||
// Sequence is good. | |||
if err != nil { | |||
t.Errorf("Expected good sequence to pass: %v", err) | |||
} | |||
// Check acc.Sequence. | |||
newAcc0 := stateCopy.GetAccount(acc0.Address) | |||
if newAcc0.Sequence != sequence { | |||
t.Errorf("Expected account sequence to change to %v, got %v", | |||
sequence, newAcc0.Sequence) | |||
} | |||
} else { | |||
// Sequence is bad. | |||
if err == nil { | |||
t.Errorf("Expected bad sequence to fail") | |||
} | |||
// Check acc.Sequence. (shouldn't have changed) | |||
newAcc0 := stateCopy.GetAccount(acc0.Address) | |||
if newAcc0.Sequence != acc0.Sequence { | |||
t.Errorf("Expected account sequence to not change from %v, got %v", | |||
acc0.Sequence, newAcc0.Sequence) | |||
} | |||
} | |||
} | |||
} | |||
func TestNameTxs(t *testing.T) { | |||
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) | |||
types.MinNameRegistrationPeriod = 5 | |||
startingBlock := state.LastBlockHeight | |||
// try some bad names. these should all fail | |||
names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), "baffledbythespectacleinallofthisyouseeehesaidwithouteyessurprised", "no spaces please"} | |||
data := "something about all this just doesn't feel right." | |||
fee := int64(1000) | |||
numDesiredBlocks := 5 | |||
for _, name := range names { | |||
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithState(state, tx, true); err == nil { | |||
t.Fatalf("Expected invalid name error from %s", name) | |||
} | |||
} | |||
// try some bad data. these should all fail | |||
name := "hold_it_chum" | |||
datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"} | |||
for _, data := range datas { | |||
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithState(state, tx, true); err == nil { | |||
t.Fatalf("Expected invalid data error from %s", data) | |||
} | |||
} | |||
validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires int) { | |||
if entry == nil { | |||
t.Fatalf("Could not find name %s", name) | |||
} | |||
if bytes.Compare(entry.Owner, addr) != 0 { | |||
t.Fatalf("Wrong owner. Got %X expected %X", entry.Owner, addr) | |||
} | |||
if data != entry.Data { | |||
t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data) | |||
} | |||
if name != entry.Name { | |||
t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name) | |||
} | |||
if expires != entry.Expires { | |||
t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires) | |||
} | |||
} | |||
// try a good one, check data, owner, expiry | |||
name = "@looking_good/karaoke_bar.broadband" | |||
data = "on this side of neptune there are 1234567890 people: first is OMNIVORE+-3. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')" | |||
amt := fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithState(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry := state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks) | |||
// fail to update it as non-owner, in same block | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithState(state, tx, true); err == nil { | |||
t.Fatal("Expected error") | |||
} | |||
// update it as owner, just to increase expiry, in same block | |||
// NOTE: we have to resend the data or it will clear it (is this what we want?) | |||
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithStateNewBlock(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*2) | |||
// update it as owner, just to increase expiry, in next block | |||
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithStateNewBlock(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*3) | |||
// fail to update it as non-owner | |||
state.LastBlockHeight = entry.Expires - 1 | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithState(state, tx, true); err == nil { | |||
t.Fatal("Expected error") | |||
} | |||
// once expires, non-owner succeeds | |||
state.LastBlockHeight = entry.Expires | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithState(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks) | |||
// update it as new owner, with new data (longer), but keep the expiry! | |||
data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that." | |||
oldCredit := amt - fee | |||
numDesiredBlocks = 10 | |||
amt = fee + (int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) - oldCredit) | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithState(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks) | |||
// test removal | |||
amt = fee | |||
data = "" | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithStateNewBlock(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
if entry != nil { | |||
t.Fatal("Expected removed entry to be nil") | |||
} | |||
// create entry by key0, | |||
// test removal by key1 after expiry | |||
name = "looking_good/karaoke_bar" | |||
data = "some data" | |||
amt = fee + int64(numDesiredBlocks)*types.NameByteCostMultiplier*types.NameBlockCostMultiplier*types.NameBaseCost(name, data) | |||
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[0]) | |||
if err := execTxWithState(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
validateEntry(t, entry, name, data, privAccounts[0].Address, state.LastBlockHeight+numDesiredBlocks) | |||
state.LastBlockHeight = entry.Expires | |||
amt = fee | |||
data = "" | |||
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) | |||
tx.Sign(state.ChainID, privAccounts[1]) | |||
if err := execTxWithStateNewBlock(state, tx, true); err != nil { | |||
t.Fatal(err) | |||
} | |||
entry = state.GetNameRegEntry(name) | |||
if entry != nil { | |||
t.Fatal("Expected removed entry to be nil") | |||
} | |||
} | |||
// TODO: test overflows. | |||
// TODO: test for unbonding validators. | |||
func TestTxs(t *testing.T) { | |||
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) | |||
//val0 := state.GetValidatorInfo(privValidators[0].Address) | |||
acc0 := state.GetAccount(privAccounts[0].PubKey.Address()) | |||
acc0PubKey := privAccounts[0].PubKey | |||
acc1 := state.GetAccount(privAccounts[1].PubKey.Address()) | |||
// SendTx. | |||
{ | |||
state := state.Copy() | |||
tx := &types.SendTx{ | |||
Inputs: []*types.TxInput{ | |||
&types.TxInput{ | |||
Address: acc0.Address, | |||
Amount: 1, | |||
Sequence: acc0.Sequence + 1, | |||
PubKey: acc0PubKey, | |||
}, | |||
}, | |||
Outputs: []*types.TxOutput{ | |||
&types.TxOutput{ | |||
Address: acc1.Address, | |||
Amount: 1, | |||
}, | |||
}, | |||
} | |||
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
err := execTxWithState(state, tx, true) | |||
if err != nil { | |||
t.Errorf("Got error in executing send transaction, %v", err) | |||
} | |||
newAcc0 := state.GetAccount(acc0.Address) | |||
if acc0.Balance-1 != newAcc0.Balance { | |||
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", | |||
acc0.Balance-1, newAcc0.Balance) | |||
} | |||
newAcc1 := state.GetAccount(acc1.Address) | |||
if acc1.Balance+1 != newAcc1.Balance { | |||
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", | |||
acc1.Balance+1, newAcc1.Balance) | |||
} | |||
} | |||
// CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more | |||
{ | |||
state := state.Copy() | |||
newAcc1 := state.GetAccount(acc1.Address) | |||
newAcc1.Code = []byte{0x60} | |||
state.UpdateAccount(newAcc1) | |||
tx := &types.CallTx{ | |||
Input: &types.TxInput{ | |||
Address: acc0.Address, | |||
Amount: 1, | |||
Sequence: acc0.Sequence + 1, | |||
PubKey: acc0PubKey, | |||
}, | |||
Address: acc1.Address, | |||
GasLimit: 10, | |||
} | |||
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
err := execTxWithState(state, tx, true) | |||
if err != nil { | |||
t.Errorf("Got error in executing call transaction, %v", err) | |||
} | |||
newAcc0 := state.GetAccount(acc0.Address) | |||
if acc0.Balance-1 != newAcc0.Balance { | |||
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", | |||
acc0.Balance-1, newAcc0.Balance) | |||
} | |||
newAcc1 = state.GetAccount(acc1.Address) | |||
if acc1.Balance+1 != newAcc1.Balance { | |||
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v", | |||
acc1.Balance+1, newAcc1.Balance) | |||
} | |||
} | |||
// NameTx. | |||
{ | |||
entryName := "satoshi" | |||
entryData := ` | |||
A purely peer-to-peer version of electronic cash would allow online | |||
payments to be sent directly from one party to another without going through a | |||
financial institution. Digital signatures provide part of the solution, but the main | |||
benefits are lost if a trusted third party is still required to prevent double-spending. | |||
We propose a solution to the double-spending problem using a peer-to-peer network. | |||
The network timestamps transactions by hashing them into an ongoing chain of | |||
hash-based proof-of-work, forming a record that cannot be changed without redoing | |||
the proof-of-work. The longest chain not only serves as proof of the sequence of | |||
events witnessed, but proof that it came from the largest pool of CPU power. As | |||
long as a majority of CPU power is controlled by nodes that are not cooperating to | |||
attack the network, they'll generate the longest chain and outpace attackers. The | |||
network itself requires minimal structure. Messages are broadcast on a best effort | |||
basis, and nodes can leave and rejoin the network at will, accepting the longest | |||
proof-of-work chain as proof of what happened while they were gone ` | |||
entryAmount := int64(10000) | |||
state := state.Copy() | |||
tx := &types.NameTx{ | |||
Input: &types.TxInput{ | |||
Address: acc0.Address, | |||
Amount: entryAmount, | |||
Sequence: acc0.Sequence + 1, | |||
PubKey: acc0PubKey, | |||
}, | |||
Name: entryName, | |||
Data: entryData, | |||
} | |||
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
err := execTxWithState(state, tx, true) | |||
if err != nil { | |||
t.Errorf("Got error in executing call transaction, %v", err) | |||
} | |||
newAcc0 := state.GetAccount(acc0.Address) | |||
if acc0.Balance-entryAmount != newAcc0.Balance { | |||
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", | |||
acc0.Balance-entryAmount, newAcc0.Balance) | |||
} | |||
entry := state.GetNameRegEntry(entryName) | |||
if entry == nil { | |||
t.Errorf("Expected an entry but got nil") | |||
} | |||
if entry.Data != entryData { | |||
t.Errorf("Wrong data stored") | |||
} | |||
// test a bad string | |||
tx.Data = string([]byte{0, 1, 2, 3, 127, 128, 129, 200, 251}) | |||
tx.Input.Sequence += 1 | |||
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
err = execTxWithState(state, tx, true) | |||
if _, ok := err.(types.ErrTxInvalidString); !ok { | |||
t.Errorf("Expected invalid string error. Got: %s", err.Error()) | |||
} | |||
} | |||
// BondTx. | |||
{ | |||
state := state.Copy() | |||
tx := &types.BondTx{ | |||
PubKey: acc0PubKey.(account.PubKeyEd25519), | |||
Inputs: []*types.TxInput{ | |||
&types.TxInput{ | |||
Address: acc0.Address, | |||
Amount: 1, | |||
Sequence: acc0.Sequence + 1, | |||
PubKey: acc0PubKey, | |||
}, | |||
}, | |||
UnbondTo: []*types.TxOutput{ | |||
&types.TxOutput{ | |||
Address: acc0.Address, | |||
Amount: 1, | |||
}, | |||
}, | |||
} | |||
tx.Signature = privAccounts[0].Sign(state.ChainID, tx).(account.SignatureEd25519) | |||
tx.Inputs[0].Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
err := execTxWithState(state, tx, true) | |||
if err != nil { | |||
t.Errorf("Got error in executing bond transaction, %v", err) | |||
} | |||
newAcc0 := state.GetAccount(acc0.Address) | |||
if newAcc0.Balance != acc0.Balance-1 { | |||
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v", | |||
acc0.Balance-1, newAcc0.Balance) | |||
} | |||
_, acc0Val := state.BondedValidators.GetByAddress(acc0.Address) | |||
if acc0Val == nil { | |||
t.Errorf("acc0Val not present") | |||
} | |||
if acc0Val.BondHeight != state.LastBlockHeight+1 { | |||
t.Errorf("Unexpected bond height. Expected %v, got %v", | |||
state.LastBlockHeight, acc0Val.BondHeight) | |||
} | |||
if acc0Val.VotingPower != 1 { | |||
t.Errorf("Unexpected voting power. Expected %v, got %v", | |||
acc0Val.VotingPower, acc0.Balance) | |||
} | |||
if acc0Val.Accum != 0 { | |||
t.Errorf("Unexpected accum. Expected 0, got %v", | |||
acc0Val.Accum) | |||
} | |||
} | |||
// TODO UnbondTx. | |||
} | |||
func TestSuicide(t *testing.T) { | |||
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) | |||
acc0 := state.GetAccount(privAccounts[0].PubKey.Address()) | |||
acc0PubKey := privAccounts[0].PubKey | |||
acc1 := state.GetAccount(privAccounts[1].PubKey.Address()) | |||
acc2 := state.GetAccount(privAccounts[2].Address) | |||
sendingAmount, refundedBalance, oldBalance := int64(1), acc1.Balance, acc2.Balance | |||
newAcc1 := state.GetAccount(acc1.Address) | |||
// store 0x1 at 0x1, push an address, then suicide :) | |||
contractCode := []byte{0x60, 0x01, 0x60, 0x01, 0x55, 0x73} | |||
contractCode = append(contractCode, acc2.Address...) | |||
contractCode = append(contractCode, 0xff) | |||
newAcc1.Code = contractCode | |||
state.UpdateAccount(newAcc1) | |||
// send call tx with no data, cause suicide | |||
tx := types.NewCallTxWithNonce(acc0PubKey, acc1.Address, nil, sendingAmount, 1000, 0, acc0.Sequence+1) | |||
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
// we use cache instead of execTxWithState so we can run the tx twice | |||
cache := NewBlockCache(state) | |||
if err := ExecTx(cache, tx, true, nil); err != nil { | |||
t.Errorf("Got error in executing call transaction, %v", err) | |||
} | |||
// if we do it again, we won't get an error, but the suicide | |||
// shouldn't happen twice and the caller should lose fee | |||
tx.Input.Sequence += 1 | |||
tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) | |||
if err := ExecTx(cache, tx, true, nil); err != nil { | |||
t.Errorf("Got error in executing call transaction, %v", err) | |||
} | |||
// commit the block | |||
cache.Sync() | |||
// acc2 should receive the sent funds and the contracts balance | |||
newAcc2 := state.GetAccount(acc2.Address) | |||
newBalance := sendingAmount + refundedBalance + oldBalance | |||
if newAcc2.Balance != newBalance { | |||
t.Errorf("Unexpected newAcc2 balance. Expected %v, got %v", | |||
newAcc2.Balance, newBalance) | |||
} | |||
newAcc1 = state.GetAccount(acc1.Address) | |||
if newAcc1 != nil { | |||
t.Errorf("Expected account to be removed") | |||
} | |||
} | |||
func TestAddValidator(t *testing.T) { | |||
// Generate a state, save & load it. | |||
s0, privAccounts, privValidators := RandGenesisState(10, false, 1000, 1, false, 1000) | |||
// The first privAccount will become a validator | |||
acc0 := privAccounts[0] | |||
bondTx := &types.BondTx{ | |||
PubKey: acc0.PubKey.(account.PubKeyEd25519), | |||
Inputs: []*types.TxInput{ | |||
&types.TxInput{ | |||
Address: acc0.Address, | |||
Amount: 1000, | |||
Sequence: 1, | |||
PubKey: acc0.PubKey, | |||
}, | |||
}, | |||
UnbondTo: []*types.TxOutput{ | |||
&types.TxOutput{ | |||
Address: acc0.Address, | |||
Amount: 1000, | |||
}, | |||
}, | |||
} | |||
bondTx.Signature = acc0.Sign(s0.ChainID, bondTx).(account.SignatureEd25519) | |||
bondTx.Inputs[0].Signature = acc0.Sign(s0.ChainID, bondTx) | |||
// Make complete block and blockParts | |||
block0 := makeBlock(t, s0, nil, []types.Tx{bondTx}) | |||
block0Parts := block0.MakePartSet() | |||
// Sanity check | |||
if s0.BondedValidators.Size() != 1 { | |||
t.Error("Expected there to be 1 validators before bondTx") | |||
} | |||
// Now append the block to s0. | |||
err := ExecBlock(s0, block0, block0Parts.Header()) | |||
if err != nil { | |||
t.Error("Error appending initial block:", err) | |||
} | |||
// Must save before further modification | |||
s0.Save() | |||
// Test new validator set | |||
if s0.BondedValidators.Size() != 2 { | |||
t.Error("Expected there to be 2 validators after bondTx") | |||
} | |||
// The validation for the next block should only require 1 signature | |||
// (the new validator wasn't active for block0) | |||
precommit0 := &types.Vote{ | |||
Height: 1, | |||
Round: 0, | |||
Type: types.VoteTypePrecommit, | |||
BlockHash: block0.Hash(), | |||
BlockPartsHeader: block0Parts.Header(), | |||
} | |||
privValidators[0].SignVote(s0.ChainID, precommit0) | |||
block1 := makeBlock(t, s0, | |||
&types.Validation{ | |||
Precommits: []*types.Vote{ | |||
precommit0, | |||
}, | |||
}, nil, | |||
) | |||
block1Parts := block1.MakePartSet() | |||
err = ExecBlock(s0, block1, block1Parts.Header()) | |||
if err != nil { | |||
t.Error("Error appending secondary block:", err) | |||
} | |||
} |
@ -1,198 +0,0 @@ | |||
package state | |||
import ( | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ... | |||
"github.com/tendermint/tendermint/types" | |||
"github.com/tendermint/tendermint/vm" | |||
) | |||
type TxCache struct { | |||
backend *BlockCache | |||
accounts map[Word256]vmAccountInfo | |||
storages map[Tuple256]Word256 | |||
} | |||
func NewTxCache(backend *BlockCache) *TxCache { | |||
return &TxCache{ | |||
backend: backend, | |||
accounts: make(map[Word256]vmAccountInfo), | |||
storages: make(map[Tuple256]Word256), | |||
} | |||
} | |||
//------------------------------------- | |||
// TxCache.account | |||
func (cache *TxCache) GetAccount(addr Word256) *vm.Account { | |||
acc, removed := cache.accounts[addr].unpack() | |||
if removed { | |||
return nil | |||
} else if acc == nil { | |||
acc2 := cache.backend.GetAccount(addr.Postfix(20)) | |||
if acc2 != nil { | |||
return toVMAccount(acc2) | |||
} | |||
} | |||
return acc | |||
} | |||
func (cache *TxCache) UpdateAccount(acc *vm.Account) { | |||
addr := acc.Address | |||
_, removed := cache.accounts[addr].unpack() | |||
if removed { | |||
PanicSanity("UpdateAccount on a removed account") | |||
} | |||
cache.accounts[addr] = vmAccountInfo{acc, false} | |||
} | |||
func (cache *TxCache) RemoveAccount(acc *vm.Account) { | |||
addr := acc.Address | |||
_, removed := cache.accounts[addr].unpack() | |||
if removed { | |||
PanicSanity("RemoveAccount on a removed account") | |||
} | |||
cache.accounts[addr] = vmAccountInfo{acc, true} | |||
} | |||
// Creates a 20 byte address and bumps the creator's nonce. | |||
func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account { | |||
// Generate an address | |||
nonce := creator.Nonce | |||
creator.Nonce += 1 | |||
addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), int(nonce))) | |||
// Create account from address. | |||
account, removed := cache.accounts[addr].unpack() | |||
if removed || account == nil { | |||
account = &vm.Account{ | |||
Address: addr, | |||
Balance: 0, | |||
Code: nil, | |||
Nonce: 0, | |||
Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress256).Permissions, | |||
Other: vmAccountOther{ | |||
PubKey: nil, | |||
StorageRoot: nil, | |||
}, | |||
} | |||
cache.accounts[addr] = vmAccountInfo{account, false} | |||
return account | |||
} else { | |||
// either we've messed up nonce handling, or sha3 is broken | |||
PanicSanity(Fmt("Could not create account, address already exists: %X", addr)) | |||
return nil | |||
} | |||
} | |||
// TxCache.account | |||
//------------------------------------- | |||
// TxCache.storage | |||
func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 { | |||
// Check cache | |||
value, ok := cache.storages[Tuple256{addr, key}] | |||
if ok { | |||
return value | |||
} | |||
// Load from backend | |||
return cache.backend.GetStorage(addr, key) | |||
} | |||
// NOTE: Set value to zero to removed from the trie. | |||
func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) { | |||
_, removed := cache.accounts[addr].unpack() | |||
if removed { | |||
PanicSanity("SetStorage() on a removed account") | |||
} | |||
cache.storages[Tuple256{addr, key}] = value | |||
} | |||
// TxCache.storage | |||
//------------------------------------- | |||
// These updates do not have to be in deterministic order, | |||
// the backend is responsible for ordering updates. | |||
func (cache *TxCache) Sync() { | |||
// Remove or update storage | |||
for addrKey, value := range cache.storages { | |||
addr, key := Tuple256Split(addrKey) | |||
cache.backend.SetStorage(addr, key, value) | |||
} | |||
// Remove or update accounts | |||
for addr, accInfo := range cache.accounts { | |||
acc, removed := accInfo.unpack() | |||
if removed { | |||
cache.backend.RemoveAccount(addr.Postfix(20)) | |||
} else { | |||
cache.backend.UpdateAccount(toStateAccount(acc)) | |||
} | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Convenience function to return address of new contract | |||
func NewContractAddress(caller []byte, nonce int) []byte { | |||
return types.NewContractAddress(caller, nonce) | |||
} | |||
// Converts backend.Account to vm.Account struct. | |||
func toVMAccount(acc *acm.Account) *vm.Account { | |||
return &vm.Account{ | |||
Address: LeftPadWord256(acc.Address), | |||
Balance: acc.Balance, | |||
Code: acc.Code, // This is crazy. | |||
Nonce: int64(acc.Sequence), | |||
Permissions: acc.Permissions, // Copy | |||
Other: vmAccountOther{ | |||
PubKey: acc.PubKey, | |||
StorageRoot: acc.StorageRoot, | |||
}, | |||
} | |||
} | |||
// Converts vm.Account to backend.Account struct. | |||
func toStateAccount(acc *vm.Account) *acm.Account { | |||
var pubKey acm.PubKey | |||
var storageRoot []byte | |||
if acc.Other != nil { | |||
pubKey, storageRoot = acc.Other.(vmAccountOther).unpack() | |||
} | |||
return &acm.Account{ | |||
Address: acc.Address.Postfix(20), | |||
PubKey: pubKey, | |||
Balance: acc.Balance, | |||
Code: acc.Code, | |||
Sequence: int(acc.Nonce), | |||
StorageRoot: storageRoot, | |||
Permissions: acc.Permissions, // Copy | |||
} | |||
} | |||
// Everything in acmAccount that doesn't belong in | |||
// exported vmAccount fields. | |||
type vmAccountOther struct { | |||
PubKey acm.PubKey | |||
StorageRoot []byte | |||
} | |||
func (accOther vmAccountOther) unpack() (acm.PubKey, []byte) { | |||
return accOther.PubKey, accOther.StorageRoot | |||
} | |||
type vmAccountInfo struct { | |||
account *vm.Account | |||
removed bool | |||
} | |||
func (accInfo vmAccountInfo) unpack() (*vm.Account, bool) { | |||
return accInfo.account, accInfo.removed | |||
} |
@ -1,23 +0,0 @@ | |||
package state | |||
import ( | |||
"bytes" | |||
"testing" | |||
stypes "github.com/tendermint/tendermint/state/types" | |||
"github.com/tendermint/go-wire" | |||
) | |||
func TestStateToFromVMAccount(t *testing.T) { | |||
acmAcc1, _ := stypes.RandAccount(true, 456) | |||
vmAcc := toVMAccount(acmAcc1) | |||
acmAcc2 := toStateAccount(vmAcc) | |||
acmAcc1Bytes := wire.BinaryBytes(acmAcc1) | |||
acmAcc2Bytes := wire.BinaryBytes(acmAcc2) | |||
if !bytes.Equal(acmAcc1Bytes, acmAcc2Bytes) { | |||
t.Errorf("Unexpected account wire bytes\n%X vs\n%X", | |||
acmAcc1Bytes, acmAcc2Bytes) | |||
} | |||
} |
@ -1,121 +0,0 @@ | |||
package types | |||
import ( | |||
"sort" | |||
"time" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
"github.com/tendermint/tendermint/types" | |||
"github.com/tendermint/go-wire" | |||
) | |||
//------------------------------------------------------------ | |||
// we store the gendoc in the db | |||
var GenDocKey = []byte("GenDocKey") | |||
//------------------------------------------------------------ | |||
// core types for a genesis definition | |||
type BasicAccount struct { | |||
Address []byte `json:"address"` | |||
Amount int64 `json:"amount"` | |||
} | |||
type GenesisAccount struct { | |||
Address []byte `json:"address"` | |||
Amount int64 `json:"amount"` | |||
Name string `json:"name"` | |||
Permissions *ptypes.AccountPermissions `json:"permissions"` | |||
} | |||
type GenesisValidator struct { | |||
PubKey acm.PubKeyEd25519 `json:"pub_key"` | |||
Amount int64 `json:"amount"` | |||
Name string `json:"name"` | |||
UnbondTo []BasicAccount `json:"unbond_to"` | |||
} | |||
type GenesisParams struct { | |||
GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"` | |||
} | |||
type GenesisDoc struct { | |||
GenesisTime time.Time `json:"genesis_time"` | |||
ChainID string `json:"chain_id"` | |||
Params *GenesisParams `json:"params"` | |||
Accounts []GenesisAccount `json:"accounts"` | |||
Validators []GenesisValidator `json:"validators"` | |||
} | |||
//------------------------------------------------------------ | |||
// Make genesis state from file | |||
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { | |||
var err error | |||
wire.ReadJSONPtr(&genState, jsonBlob, &err) | |||
if err != nil { | |||
Exit(Fmt("Couldn't read GenesisDoc: %v", err)) | |||
} | |||
return | |||
} | |||
//------------------------------------------------------------ | |||
// Make random genesis state | |||
func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAccount) { | |||
privAccount := acm.GenPrivAccount() | |||
perms := ptypes.DefaultAccountPermissions | |||
acc := &acm.Account{ | |||
Address: privAccount.PubKey.Address(), | |||
PubKey: privAccount.PubKey, | |||
Sequence: RandInt(), | |||
Balance: minBalance, | |||
Permissions: perms, | |||
} | |||
if randBalance { | |||
acc.Balance += int64(RandUint32()) | |||
} | |||
return acc, privAccount | |||
} | |||
func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) { | |||
accounts := make([]GenesisAccount, numAccounts) | |||
privAccounts := make([]*acm.PrivAccount, numAccounts) | |||
defaultPerms := ptypes.DefaultAccountPermissions | |||
for i := 0; i < numAccounts; i++ { | |||
account, privAccount := RandAccount(randBalance, minBalance) | |||
accounts[i] = GenesisAccount{ | |||
Address: account.Address, | |||
Amount: account.Balance, | |||
Permissions: &defaultPerms, // This will get copied into each state.Account. | |||
} | |||
privAccounts[i] = privAccount | |||
} | |||
validators := make([]GenesisValidator, numValidators) | |||
privValidators := make([]*types.PrivValidator, numValidators) | |||
for i := 0; i < numValidators; i++ { | |||
valInfo, _, privVal := types.RandValidator(randBonded, minBonded) | |||
validators[i] = GenesisValidator{ | |||
PubKey: valInfo.PubKey, | |||
Amount: valInfo.FirstBondAmount, | |||
UnbondTo: []BasicAccount{ | |||
{ | |||
Address: valInfo.PubKey.Address(), | |||
Amount: valInfo.FirstBondAmount, | |||
}, | |||
}, | |||
} | |||
privValidators[i] = privVal | |||
} | |||
sort.Sort(types.PrivValidatorsByAddress(privValidators)) | |||
return &GenesisDoc{ | |||
GenesisTime: time.Now(), | |||
ChainID: "tendermint_test", | |||
Accounts: accounts, | |||
Validators: validators, | |||
}, privAccounts, privValidators | |||
} |
@ -0,0 +1,65 @@ | |||
package types | |||
import ( | |||
"sort" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/go-crypto" | |||
"github.com/tendermint/go-wire" | |||
) | |||
//------------------------------------------------------------ | |||
// we store the gendoc in the db | |||
var GenDocKey = []byte("GenDocKey") | |||
//------------------------------------------------------------ | |||
// core types for a genesis definition | |||
type GenesisValidator struct { | |||
PubKey crypto.PubKeyEd25519 `json:"pub_key"` | |||
Amount int64 `json:"amount"` | |||
Name string `json:"name"` | |||
} | |||
type GenesisDoc struct { | |||
GenesisTime time.Time `json:"genesis_time"` | |||
ChainID string `json:"chain_id"` | |||
Validators []GenesisValidator `json:"validators"` | |||
} | |||
//------------------------------------------------------------ | |||
// Make genesis state from file | |||
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { | |||
var err error | |||
wire.ReadJSONPtr(&genState, jsonBlob, &err) | |||
if err != nil { | |||
Exit(Fmt("Couldn't read GenesisDoc: %v", err)) | |||
} | |||
return | |||
} | |||
//------------------------------------------------------------ | |||
// Make random genesis state | |||
func RandGenesisDoc(numValidators int, randPower bool, minPower int64) (*GenesisDoc, []*PrivValidator) { | |||
validators := make([]GenesisValidator, numValidators) | |||
privValidators := make([]*PrivValidator, numValidators) | |||
for i := 0; i < numValidators; i++ { | |||
val, privVal := RandValidator(randPower, minPower) | |||
validators[i] = GenesisValidator{ | |||
PubKey: val.PubKey, | |||
Amount: val.VotingPower, | |||
} | |||
privValidators[i] = privVal | |||
} | |||
sort.Sort(PrivValidatorsByAddress(privValidators)) | |||
return &GenesisDoc{ | |||
GenesisTime: time.Now(), | |||
ChainID: "tendermint_test", | |||
Validators: validators, | |||
}, privValidators | |||
} |
@ -1,55 +0,0 @@ | |||
package types | |||
import ( | |||
"regexp" | |||
) | |||
var ( | |||
MinNameRegistrationPeriod int = 5 | |||
// NOTE: base costs and validity checks are here so clients | |||
// can use them without importing state | |||
// cost for storing a name for a block is | |||
// CostPerBlock*CostPerByte*(len(data) + 32) | |||
NameByteCostMultiplier int64 = 1 | |||
NameBlockCostMultiplier int64 = 1 | |||
MaxNameLength = 64 | |||
MaxDataLength = 1 << 16 | |||
// Name should be file system lik | |||
// Data should be anything permitted in JSON | |||
regexpAlphaNum = regexp.MustCompile("^[a-zA-Z0-9._/-@]*$") | |||
regexpJSON = regexp.MustCompile(`^[a-zA-Z0-9_/ \-+"':,\n\t.{}()\[\]]*$`) | |||
) | |||
// filter strings | |||
func validateNameRegEntryName(name string) bool { | |||
return regexpAlphaNum.Match([]byte(name)) | |||
} | |||
func validateNameRegEntryData(data string) bool { | |||
return regexpJSON.Match([]byte(data)) | |||
} | |||
// base cost is "effective" number of bytes | |||
func NameBaseCost(name, data string) int64 { | |||
return int64(len(data) + 32) | |||
} | |||
func NameCostPerBlock(baseCost int64) int64 { | |||
return NameBlockCostMultiplier * NameByteCostMultiplier * baseCost | |||
} | |||
type NameRegEntry struct { | |||
Name string `json:"name"` // registered name for the entry | |||
Owner []byte `json:"owner"` // address that created the entry | |||
Data string `json:"data"` // data to store under this name | |||
Expires int `json:"expires"` // block at which this entry expires | |||
} | |||
func (entry *NameRegEntry) Copy() *NameRegEntry { | |||
entryCopy := *entry | |||
return &entryCopy | |||
} |
@ -1,67 +0,0 @@ | |||
package types | |||
import ( | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
"strings" | |||
) | |||
type NodeInfo struct { | |||
PubKey acm.PubKeyEd25519 `json:"pub_key"` | |||
Moniker string `json:"moniker"` | |||
ChainID string `json:"chain_id"` | |||
Host string `json:"host"` | |||
P2PPort uint16 `json:"p2p_port"` | |||
RPCPort uint16 `json:"rpc_port"` | |||
Version Versions `json:"versions"` | |||
} | |||
type Versions struct { | |||
Revision string `json:"revision"` | |||
Tendermint string `json:"tendermint"` | |||
P2P string `json:"p2p"` | |||
RPC string `json:"rpc"` | |||
Wire string `json:"wire"` | |||
} | |||
// CONTRACT: two nodes with the same Tendermint major and minor version and with the same ChainID are compatible | |||
func (ni *NodeInfo) CompatibleWith(no *NodeInfo) error { | |||
iM, im, _, ie := splitVersion(ni.Version.Tendermint) | |||
oM, om, _, oe := splitVersion(no.Version.Tendermint) | |||
// if our own version number is not formatted right, we messed up | |||
if ie != nil { | |||
return ie | |||
} | |||
// version number must be formatted correctly ("x.x.x") | |||
if oe != nil { | |||
return oe | |||
} | |||
// major version must match | |||
if iM != oM { | |||
return fmt.Errorf("Peer is on a different major version. Got %v, expected %v", oM, iM) | |||
} | |||
// minor version must match | |||
if im != om { | |||
return fmt.Errorf("Peer is on a different minor version. Got %v, expected %v", om, im) | |||
} | |||
// nodes must be on the same chain_id | |||
if ni.ChainID != no.ChainID { | |||
return fmt.Errorf("Peer is on a different chain_id. Got %v, expected %v", no.ChainID, ni.ChainID) | |||
} | |||
return nil | |||
} | |||
func splitVersion(version string) (string, string, string, error) { | |||
spl := strings.Split(version, ".") | |||
if len(spl) != 3 { | |||
return "", "", "", fmt.Errorf("Invalid version format %v", version) | |||
} | |||
return spl[0], spl[1], spl[2], nil | |||
} |
@ -0,0 +1,30 @@ | |||
package types | |||
import ( | |||
"bytes" | |||
"io" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/go-merkle" | |||
) | |||
// Signable is an interface for all signable things. | |||
// It typically removes signatures before serializing. | |||
type Signable interface { | |||
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) | |||
} | |||
// SignBytes is a convenience method for getting the bytes to sign of a Signable. | |||
func SignBytes(chainID string, o Signable) []byte { | |||
buf, n, err := new(bytes.Buffer), new(int64), new(error) | |||
o.WriteSignBytes(chainID, buf, n, err) | |||
if *err != nil { | |||
PanicCrisis(err) | |||
} | |||
return buf.Bytes() | |||
} | |||
// HashSignBytes is a convenience method for getting the hash of the bytes of a signable | |||
func HashSignBytes(chainID string, o Signable) []byte { | |||
return merkle.SimpleHashFromBinary(SignBytes(chainID, o)) | |||
} |
@ -1,375 +1,3 @@ | |||
package types | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"io" | |||
"golang.org/x/crypto/ripemd160" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
"github.com/tendermint/go-wire" | |||
) | |||
var ( | |||
ErrTxInvalidAddress = errors.New("Error invalid address") | |||
ErrTxDuplicateAddress = errors.New("Error duplicate address") | |||
ErrTxInvalidAmount = errors.New("Error invalid amount") | |||
ErrTxInsufficientFunds = errors.New("Error insufficient funds") | |||
ErrTxInsufficientGasPrice = errors.New("Error insufficient gas price") | |||
ErrTxUnknownPubKey = errors.New("Error unknown pubkey") | |||
ErrTxInvalidPubKey = errors.New("Error invalid pubkey") | |||
ErrTxInvalidSignature = errors.New("Error invalid signature") | |||
ErrTxPermissionDenied = errors.New("Error permission denied") | |||
) | |||
type ErrTxInvalidString struct { | |||
Msg string | |||
} | |||
func (e ErrTxInvalidString) Error() string { | |||
return e.Msg | |||
} | |||
type ErrTxInvalidSequence struct { | |||
Got int | |||
Expected int | |||
} | |||
func (e ErrTxInvalidSequence) Error() string { | |||
return Fmt("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected) | |||
} | |||
/* | |||
Tx (Transaction) is an atomic operation on the ledger state. | |||
Account Txs: | |||
- SendTx Send coins to address | |||
- CallTx Send a msg to a contract that runs in the vm | |||
- NameTx Store some value under a name in the global namereg | |||
Validation Txs: | |||
- BondTx New validator posts a bond | |||
- UnbondTx Validator leaves | |||
- DupeoutTx Validator dupes out (equivocates) | |||
Admin Txs: | |||
- PermissionsTx | |||
*/ | |||
type Tx interface { | |||
WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) | |||
} | |||
// Types of Tx implementations | |||
const ( | |||
// Account transactions | |||
TxTypeSend = byte(0x01) | |||
TxTypeCall = byte(0x02) | |||
TxTypeName = byte(0x03) | |||
// Validation transactions | |||
TxTypeBond = byte(0x11) | |||
TxTypeUnbond = byte(0x12) | |||
TxTypeRebond = byte(0x13) | |||
TxTypeDupeout = byte(0x14) | |||
// Admin transactions | |||
TxTypePermissions = byte(0x20) | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ Tx }{}, | |||
wire.ConcreteType{&SendTx{}, TxTypeSend}, | |||
wire.ConcreteType{&CallTx{}, TxTypeCall}, | |||
wire.ConcreteType{&NameTx{}, TxTypeName}, | |||
wire.ConcreteType{&BondTx{}, TxTypeBond}, | |||
wire.ConcreteType{&UnbondTx{}, TxTypeUnbond}, | |||
wire.ConcreteType{&RebondTx{}, TxTypeRebond}, | |||
wire.ConcreteType{&DupeoutTx{}, TxTypeDupeout}, | |||
wire.ConcreteType{&PermissionsTx{}, TxTypePermissions}, | |||
) | |||
//----------------------------------------------------------------------------- | |||
type TxInput struct { | |||
Address []byte `json:"address"` // Hash of the PubKey | |||
Amount int64 `json:"amount"` // Must not exceed account balance | |||
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput | |||
Signature acm.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx | |||
PubKey acm.PubKey `json:"pub_key"` // Must not be nil, may be nil | |||
} | |||
func (txIn *TxInput) ValidateBasic() error { | |||
if len(txIn.Address) != 20 { | |||
return ErrTxInvalidAddress | |||
} | |||
if txIn.Amount == 0 { | |||
return ErrTxInvalidAmount | |||
} | |||
return nil | |||
} | |||
func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v,"sequence":%v}`, txIn.Address, txIn.Amount, txIn.Sequence)), w, n, err) | |||
} | |||
func (txIn *TxInput) String() string { | |||
return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type TxOutput struct { | |||
Address []byte `json:"address"` // Hash of the PubKey | |||
Amount int64 `json:"amount"` // The sum of all outputs must not exceed the inputs. | |||
} | |||
func (txOut *TxOutput) ValidateBasic() error { | |||
if len(txOut.Address) != 20 { | |||
return ErrTxInvalidAddress | |||
} | |||
if txOut.Amount == 0 { | |||
return ErrTxInvalidAmount | |||
} | |||
return nil | |||
} | |||
func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"address":"%X","amount":%v}`, txOut.Address, txOut.Amount)), w, n, err) | |||
} | |||
func (txOut *TxOutput) String() string { | |||
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type SendTx struct { | |||
Inputs []*TxInput `json:"inputs"` | |||
Outputs []*TxOutput `json:"outputs"` | |||
} | |||
func (tx *SendTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)), w, n, err) | |||
for i, in := range tx.Inputs { | |||
in.WriteSignBytes(w, n, err) | |||
if i != len(tx.Inputs)-1 { | |||
wire.WriteTo([]byte(","), w, n, err) | |||
} | |||
} | |||
wire.WriteTo([]byte(`],"outputs":[`), w, n, err) | |||
for i, out := range tx.Outputs { | |||
out.WriteSignBytes(w, n, err) | |||
if i != len(tx.Outputs)-1 { | |||
wire.WriteTo([]byte(","), w, n, err) | |||
} | |||
} | |||
wire.WriteTo([]byte(`]}]}`), w, n, err) | |||
} | |||
func (tx *SendTx) String() string { | |||
return Fmt("SendTx{%v -> %v}", tx.Inputs, tx.Outputs) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type CallTx struct { | |||
Input *TxInput `json:"input"` | |||
Address []byte `json:"address"` | |||
GasLimit int64 `json:"gas_limit"` | |||
Fee int64 `json:"fee"` | |||
Data []byte `json:"data"` | |||
} | |||
func (tx *CallTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)), w, n, err) | |||
tx.Input.WriteSignBytes(w, n, err) | |||
wire.WriteTo([]byte(`}]}`), w, n, err) | |||
} | |||
func (tx *CallTx) String() string { | |||
return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data) | |||
} | |||
func NewContractAddress(caller []byte, nonce int) []byte { | |||
temp := make([]byte, 32+8) | |||
copy(temp, caller) | |||
PutInt64BE(temp[32:], int64(nonce)) | |||
hasher := ripemd160.New() | |||
hasher.Write(temp) // does not error | |||
return hasher.Sum(nil) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type NameTx struct { | |||
Input *TxInput `json:"input"` | |||
Name string `json:"name"` | |||
Data string `json:"data"` | |||
Fee int64 `json:"fee"` | |||
} | |||
func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"data":%s,"fee":%v`, TxTypeName, jsonEscape(tx.Data), tx.Fee)), w, n, err) | |||
wire.WriteTo([]byte(`,"input":`), w, n, err) | |||
tx.Input.WriteSignBytes(w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"name":%s`, jsonEscape(tx.Name))), w, n, err) | |||
wire.WriteTo([]byte(`}]}`), w, n, err) | |||
} | |||
func (tx *NameTx) ValidateStrings() error { | |||
if len(tx.Name) == 0 { | |||
return ErrTxInvalidString{"Name must not be empty"} | |||
} | |||
if len(tx.Name) > MaxNameLength { | |||
return ErrTxInvalidString{Fmt("Name is too long. Max %d bytes", MaxNameLength)} | |||
} | |||
if len(tx.Data) > MaxDataLength { | |||
return ErrTxInvalidString{Fmt("Data is too long. Max %d bytes", MaxDataLength)} | |||
} | |||
if !validateNameRegEntryName(tx.Name) { | |||
return ErrTxInvalidString{Fmt("Invalid characters found in NameTx.Name (%s). Only alphanumeric, underscores, dashes, forward slashes, and @ are allowed", tx.Name)} | |||
} | |||
if !validateNameRegEntryData(tx.Data) { | |||
return ErrTxInvalidString{Fmt("Invalid characters found in NameTx.Data (%s). Only the kind of things found in a JSON file are allowed", tx.Data)} | |||
} | |||
return nil | |||
} | |||
func (tx *NameTx) String() string { | |||
return Fmt("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type BondTx struct { | |||
PubKey acm.PubKeyEd25519 `json:"pub_key"` | |||
Signature acm.SignatureEd25519 `json:"signature"` | |||
Inputs []*TxInput `json:"inputs"` | |||
UnbondTo []*TxOutput `json:"unbond_to"` | |||
} | |||
func (tx *BondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeBond)), w, n, err) | |||
for i, in := range tx.Inputs { | |||
in.WriteSignBytes(w, n, err) | |||
if i != len(tx.Inputs)-1 { | |||
wire.WriteTo([]byte(","), w, n, err) | |||
} | |||
} | |||
wire.WriteTo([]byte(Fmt(`],"pub_key":`)), w, n, err) | |||
wire.WriteTo(wire.JSONBytes(tx.PubKey), w, n, err) | |||
wire.WriteTo([]byte(`,"unbond_to":[`), w, n, err) | |||
for i, out := range tx.UnbondTo { | |||
out.WriteSignBytes(w, n, err) | |||
if i != len(tx.UnbondTo)-1 { | |||
wire.WriteTo([]byte(","), w, n, err) | |||
} | |||
} | |||
wire.WriteTo([]byte(`]}]}`), w, n, err) | |||
} | |||
func (tx *BondTx) String() string { | |||
return Fmt("BondTx{%v: %v -> %v}", tx.PubKey, tx.Inputs, tx.UnbondTo) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type UnbondTx struct { | |||
Address []byte `json:"address"` | |||
Height int `json:"height"` | |||
Signature acm.SignatureEd25519 `json:"signature"` | |||
} | |||
func (tx *UnbondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeUnbond, tx.Address, tx.Height)), w, n, err) | |||
} | |||
func (tx *UnbondTx) String() string { | |||
return Fmt("UnbondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type RebondTx struct { | |||
Address []byte `json:"address"` | |||
Height int `json:"height"` | |||
Signature acm.SignatureEd25519 `json:"signature"` | |||
} | |||
func (tx *RebondTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"address":"%X","height":%v}]}`, TxTypeRebond, tx.Address, tx.Height)), w, n, err) | |||
} | |||
func (tx *RebondTx) String() string { | |||
return Fmt("RebondTx{%X,%v,%v}", tx.Address, tx.Height, tx.Signature) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type DupeoutTx struct { | |||
Address []byte `json:"address"` | |||
VoteA Vote `json:"vote_a"` | |||
VoteB Vote `json:"vote_b"` | |||
} | |||
func (tx *DupeoutTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
PanicSanity("DupeoutTx has no sign bytes") | |||
} | |||
func (tx *DupeoutTx) String() string { | |||
return Fmt("DupeoutTx{%X,%v,%v}", tx.Address, tx.VoteA, tx.VoteB) | |||
} | |||
//----------------------------------------------------------------------------- | |||
type PermissionsTx struct { | |||
Input *TxInput `json:"input"` | |||
PermArgs ptypes.PermArgs `json:"args"` | |||
} | |||
func (tx *PermissionsTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { | |||
wire.WriteTo([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))), w, n, err) | |||
wire.WriteTo([]byte(Fmt(`,"tx":[%v,{"args":"`, TxTypePermissions)), w, n, err) | |||
wire.WriteJSON(tx.PermArgs, w, n, err) | |||
wire.WriteTo([]byte(`","input":`), w, n, err) | |||
tx.Input.WriteSignBytes(w, n, err) | |||
wire.WriteTo([]byte(`}]}`), w, n, err) | |||
} | |||
func (tx *PermissionsTx) String() string { | |||
return Fmt("PermissionsTx{%v -> %v}", tx.Input, tx.PermArgs) | |||
} | |||
//----------------------------------------------------------------------------- | |||
// This should match the leaf hashes of Block.Data.Hash()'s SimpleMerkleTree. | |||
func TxID(chainID string, tx Tx) []byte { | |||
signBytes := acm.SignBytes(chainID, tx) | |||
return wire.BinaryRipemd160(signBytes) | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// Contract: This function is deterministic and completely reversible. | |||
func jsonEscape(str string) string { | |||
escapedBytes, err := json.Marshal(str) | |||
if err != nil { | |||
PanicSanity(Fmt("Error json-escaping a string", str)) | |||
} | |||
return string(escapedBytes) | |||
} | |||
type Tx []byte |
@ -1,178 +0,0 @@ | |||
package types | |||
import ( | |||
"testing" | |||
acm "github.com/tendermint/tendermint/account" | |||
. "github.com/tendermint/go-common" | |||
_ "github.com/tendermint/tendermint/config/tendermint_test" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
) | |||
var chainID string | |||
func init() { | |||
chainID = config.GetString("chain_id") | |||
} | |||
func TestSendTxSignable(t *testing.T) { | |||
sendTx := &SendTx{ | |||
Inputs: []*TxInput{ | |||
&TxInput{ | |||
Address: []byte("input1"), | |||
Amount: 12345, | |||
Sequence: 67890, | |||
}, | |||
&TxInput{ | |||
Address: []byte("input2"), | |||
Amount: 111, | |||
Sequence: 222, | |||
}, | |||
}, | |||
Outputs: []*TxOutput{ | |||
&TxOutput{ | |||
Address: []byte("output1"), | |||
Amount: 333, | |||
}, | |||
&TxOutput{ | |||
Address: []byte("output2"), | |||
Amount: 444, | |||
}, | |||
}, | |||
} | |||
signBytes := acm.SignBytes(chainID, sendTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr) | |||
} | |||
} | |||
func TestCallTxSignable(t *testing.T) { | |||
callTx := &CallTx{ | |||
Input: &TxInput{ | |||
Address: []byte("input1"), | |||
Amount: 12345, | |||
Sequence: 67890, | |||
}, | |||
Address: []byte("contract1"), | |||
GasLimit: 111, | |||
Fee: 222, | |||
Data: []byte("data1"), | |||
} | |||
signBytes := acm.SignBytes(chainID, callTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr) | |||
} | |||
} | |||
func TestNameTxSignable(t *testing.T) { | |||
nameTx := &NameTx{ | |||
Input: &TxInput{ | |||
Address: []byte("input1"), | |||
Amount: 12345, | |||
Sequence: 250, | |||
}, | |||
Name: "google.com", | |||
Data: "secretly.not.google.com", | |||
Fee: 1000, | |||
} | |||
signBytes := acm.SignBytes(chainID, nameTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[3,{"data":"secretly.not.google.com","fee":1000,"input":{"address":"696E70757431","amount":12345,"sequence":250},"name":"google.com"}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr) | |||
} | |||
} | |||
func TestBondTxSignable(t *testing.T) { | |||
privKeyBytes := make([]byte, 64) | |||
privAccount := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) | |||
bondTx := &BondTx{ | |||
PubKey: privAccount.PubKey.(acm.PubKeyEd25519), | |||
Inputs: []*TxInput{ | |||
&TxInput{ | |||
Address: []byte("input1"), | |||
Amount: 12345, | |||
Sequence: 67890, | |||
}, | |||
&TxInput{ | |||
Address: []byte("input2"), | |||
Amount: 111, | |||
Sequence: 222, | |||
}, | |||
}, | |||
UnbondTo: []*TxOutput{ | |||
&TxOutput{ | |||
Address: []byte("output1"), | |||
Amount: 333, | |||
}, | |||
&TxOutput{ | |||
Address: []byte("output2"), | |||
Amount: 444, | |||
}, | |||
}, | |||
} | |||
signBytes := acm.SignBytes(chainID, bondTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[17,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"pub_key":[1,"3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29"],"unbond_to":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Unexpected sign string for BondTx. \nGot %s\nExpected %s", signStr, expected) | |||
} | |||
} | |||
func TestUnbondTxSignable(t *testing.T) { | |||
unbondTx := &UnbondTx{ | |||
Address: []byte("address1"), | |||
Height: 111, | |||
} | |||
signBytes := acm.SignBytes(chainID, unbondTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[18,{"address":"6164647265737331","height":111}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for UnbondTx") | |||
} | |||
} | |||
func TestRebondTxSignable(t *testing.T) { | |||
rebondTx := &RebondTx{ | |||
Address: []byte("address1"), | |||
Height: 111, | |||
} | |||
signBytes := acm.SignBytes(chainID, rebondTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[19,{"address":"6164647265737331","height":111}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for RebondTx") | |||
} | |||
} | |||
func TestPermissionsTxSignable(t *testing.T) { | |||
permsTx := &PermissionsTx{ | |||
Input: &TxInput{ | |||
Address: []byte("input1"), | |||
Amount: 12345, | |||
Sequence: 250, | |||
}, | |||
PermArgs: &ptypes.SetBaseArgs{ | |||
Address: []byte("address1"), | |||
Permission: 1, | |||
Value: true, | |||
}, | |||
} | |||
signBytes := acm.SignBytes(chainID, permsTx) | |||
signStr := string(signBytes) | |||
expected := Fmt(`{"chain_id":"%s","tx":[32,{"args":"[2,{"address":"6164647265737331","permission":1,"value":true}]","input":{"address":"696E70757431","amount":12345,"sequence":250}}]}`, | |||
config.GetString("chain_id")) | |||
if signStr != expected { | |||
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr) | |||
} | |||
} |
@ -1,260 +0,0 @@ | |||
package types | |||
import ( | |||
"fmt" | |||
acm "github.com/tendermint/tendermint/account" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
) | |||
type AccountGetter interface { | |||
GetAccount(addr []byte) *acm.Account | |||
} | |||
//---------------------------------------------------------------------------- | |||
// SendTx interface for adding inputs/outputs and adding signatures | |||
func NewSendTx() *SendTx { | |||
return &SendTx{ | |||
Inputs: []*TxInput{}, | |||
Outputs: []*TxOutput{}, | |||
} | |||
} | |||
func (tx *SendTx) AddInput(st AccountGetter, pubkey acm.PubKey, amt int64) error { | |||
addr := pubkey.Address() | |||
acc := st.GetAccount(addr) | |||
if acc == nil { | |||
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey) | |||
} | |||
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1) | |||
} | |||
func (tx *SendTx) AddInputWithNonce(pubkey acm.PubKey, amt int64, nonce int) error { | |||
addr := pubkey.Address() | |||
tx.Inputs = append(tx.Inputs, &TxInput{ | |||
Address: addr, | |||
Amount: amt, | |||
Sequence: nonce, | |||
Signature: acm.SignatureEd25519{}, | |||
PubKey: pubkey, | |||
}) | |||
return nil | |||
} | |||
func (tx *SendTx) AddOutput(addr []byte, amt int64) error { | |||
tx.Outputs = append(tx.Outputs, &TxOutput{ | |||
Address: addr, | |||
Amount: amt, | |||
}) | |||
return nil | |||
} | |||
func (tx *SendTx) SignInput(chainID string, i int, privAccount *acm.PrivAccount) error { | |||
if i >= len(tx.Inputs) { | |||
return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs)) | |||
} | |||
tx.Inputs[i].PubKey = privAccount.PubKey | |||
tx.Inputs[i].Signature = privAccount.Sign(chainID, tx) | |||
return nil | |||
} | |||
//---------------------------------------------------------------------------- | |||
// CallTx interface for creating tx | |||
func NewCallTx(st AccountGetter, from acm.PubKey, to, data []byte, amt, gasLimit, fee int64) (*CallTx, error) { | |||
addr := from.Address() | |||
acc := st.GetAccount(addr) | |||
if acc == nil { | |||
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from) | |||
} | |||
nonce := acc.Sequence + 1 | |||
return NewCallTxWithNonce(from, to, data, amt, gasLimit, fee, nonce), nil | |||
} | |||
func NewCallTxWithNonce(from acm.PubKey, to, data []byte, amt, gasLimit, fee int64, nonce int) *CallTx { | |||
addr := from.Address() | |||
input := &TxInput{ | |||
Address: addr, | |||
Amount: amt, | |||
Sequence: nonce, | |||
Signature: acm.SignatureEd25519{}, | |||
PubKey: from, | |||
} | |||
return &CallTx{ | |||
Input: input, | |||
Address: to, | |||
GasLimit: gasLimit, | |||
Fee: fee, | |||
Data: data, | |||
} | |||
} | |||
func (tx *CallTx) Sign(chainID string, privAccount *acm.PrivAccount) { | |||
tx.Input.PubKey = privAccount.PubKey | |||
tx.Input.Signature = privAccount.Sign(chainID, tx) | |||
} | |||
//---------------------------------------------------------------------------- | |||
// NameTx interface for creating tx | |||
func NewNameTx(st AccountGetter, from acm.PubKey, name, data string, amt, fee int64) (*NameTx, error) { | |||
addr := from.Address() | |||
acc := st.GetAccount(addr) | |||
if acc == nil { | |||
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from) | |||
} | |||
nonce := acc.Sequence + 1 | |||
return NewNameTxWithNonce(from, name, data, amt, fee, nonce), nil | |||
} | |||
func NewNameTxWithNonce(from acm.PubKey, name, data string, amt, fee int64, nonce int) *NameTx { | |||
addr := from.Address() | |||
input := &TxInput{ | |||
Address: addr, | |||
Amount: amt, | |||
Sequence: nonce, | |||
Signature: acm.SignatureEd25519{}, | |||
PubKey: from, | |||
} | |||
return &NameTx{ | |||
Input: input, | |||
Name: name, | |||
Data: data, | |||
Fee: fee, | |||
} | |||
} | |||
func (tx *NameTx) Sign(chainID string, privAccount *acm.PrivAccount) { | |||
tx.Input.PubKey = privAccount.PubKey | |||
tx.Input.Signature = privAccount.Sign(chainID, tx) | |||
} | |||
//---------------------------------------------------------------------------- | |||
// BondTx interface for adding inputs/outputs and adding signatures | |||
func NewBondTx(pubkey acm.PubKey) (*BondTx, error) { | |||
pubkeyEd, ok := pubkey.(acm.PubKeyEd25519) | |||
if !ok { | |||
return nil, fmt.Errorf("Pubkey must be ed25519") | |||
} | |||
return &BondTx{ | |||
PubKey: pubkeyEd, | |||
Inputs: []*TxInput{}, | |||
UnbondTo: []*TxOutput{}, | |||
}, nil | |||
} | |||
func (tx *BondTx) AddInput(st AccountGetter, pubkey acm.PubKey, amt int64) error { | |||
addr := pubkey.Address() | |||
acc := st.GetAccount(addr) | |||
if acc == nil { | |||
return fmt.Errorf("Invalid address %X from pubkey %X", addr, pubkey) | |||
} | |||
return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1) | |||
} | |||
func (tx *BondTx) AddInputWithNonce(pubkey acm.PubKey, amt int64, nonce int) error { | |||
addr := pubkey.Address() | |||
tx.Inputs = append(tx.Inputs, &TxInput{ | |||
Address: addr, | |||
Amount: amt, | |||
Sequence: nonce, | |||
Signature: acm.SignatureEd25519{}, | |||
PubKey: pubkey, | |||
}) | |||
return nil | |||
} | |||
func (tx *BondTx) AddOutput(addr []byte, amt int64) error { | |||
tx.UnbondTo = append(tx.UnbondTo, &TxOutput{ | |||
Address: addr, | |||
Amount: amt, | |||
}) | |||
return nil | |||
} | |||
func (tx *BondTx) SignBond(chainID string, privAccount *acm.PrivAccount) error { | |||
sig := privAccount.Sign(chainID, tx) | |||
sigEd, ok := sig.(acm.SignatureEd25519) | |||
if !ok { | |||
return fmt.Errorf("Bond signer must be ED25519") | |||
} | |||
tx.Signature = sigEd | |||
return nil | |||
} | |||
func (tx *BondTx) SignInput(chainID string, i int, privAccount *acm.PrivAccount) error { | |||
if i >= len(tx.Inputs) { | |||
return fmt.Errorf("Index %v is greater than number of inputs (%v)", i, len(tx.Inputs)) | |||
} | |||
tx.Inputs[i].PubKey = privAccount.PubKey | |||
tx.Inputs[i].Signature = privAccount.Sign(chainID, tx) | |||
return nil | |||
} | |||
//---------------------------------------------------------------------- | |||
// UnbondTx interface for creating tx | |||
func NewUnbondTx(addr []byte, height int) *UnbondTx { | |||
return &UnbondTx{ | |||
Address: addr, | |||
Height: height, | |||
} | |||
} | |||
func (tx *UnbondTx) Sign(chainID string, privAccount *acm.PrivAccount) { | |||
tx.Signature = privAccount.Sign(chainID, tx).(acm.SignatureEd25519) | |||
} | |||
//---------------------------------------------------------------------- | |||
// RebondTx interface for creating tx | |||
func NewRebondTx(addr []byte, height int) *RebondTx { | |||
return &RebondTx{ | |||
Address: addr, | |||
Height: height, | |||
} | |||
} | |||
func (tx *RebondTx) Sign(chainID string, privAccount *acm.PrivAccount) { | |||
tx.Signature = privAccount.Sign(chainID, tx).(acm.SignatureEd25519) | |||
} | |||
//---------------------------------------------------------------------------- | |||
// PermissionsTx interface for creating tx | |||
func NewPermissionsTx(st AccountGetter, from acm.PubKey, args ptypes.PermArgs) (*PermissionsTx, error) { | |||
addr := from.Address() | |||
acc := st.GetAccount(addr) | |||
if acc == nil { | |||
return nil, fmt.Errorf("Invalid address %X from pubkey %X", addr, from) | |||
} | |||
nonce := acc.Sequence + 1 | |||
return NewPermissionsTxWithNonce(from, args, nonce), nil | |||
} | |||
func NewPermissionsTxWithNonce(from acm.PubKey, args ptypes.PermArgs, nonce int) *PermissionsTx { | |||
addr := from.Address() | |||
input := &TxInput{ | |||
Address: addr, | |||
Amount: 1, // NOTE: amounts can't be 0 ... | |||
Sequence: nonce, | |||
Signature: acm.SignatureEd25519{}, | |||
PubKey: from, | |||
} | |||
return &PermissionsTx{ | |||
Input: input, | |||
PermArgs: args, | |||
} | |||
} | |||
func (tx *PermissionsTx) Sign(chainID string, privAccount *acm.PrivAccount) { | |||
tx.Input.PubKey = privAccount.PubKey | |||
tx.Input.Signature = privAccount.Sign(chainID, tx) | |||
} |
@ -1,26 +0,0 @@ | |||
package vm | |||
import ( | |||
"math/big" | |||
) | |||
// To256 | |||
// | |||
// "cast" the big int to a 256 big int (i.e., limit to) | |||
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) | |||
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) | |||
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) | |||
func U256(x *big.Int) *big.Int { | |||
x.And(x, tt256m1) | |||
return x | |||
} | |||
func S256(x *big.Int) *big.Int { | |||
if x.Cmp(tt255) < 0 { | |||
return x | |||
} else { | |||
// We don't want to modify x, ever | |||
return new(big.Int).Sub(x, tt256) | |||
} | |||
} |
@ -1,18 +0,0 @@ | |||
package vm | |||
const ( | |||
GasSha3 int64 = 1 | |||
GasGetAccount int64 = 1 | |||
GasStorageUpdate int64 = 1 | |||
GasBaseOp int64 = 0 // TODO: make this 1 | |||
GasStackOp int64 = 1 | |||
GasEcRecover int64 = 1 | |||
GasSha256Word int64 = 1 | |||
GasSha256Base int64 = 1 | |||
GasRipemd160Word int64 = 1 | |||
GasRipemd160Base int64 = 1 | |||
GasIdentityWord int64 = 1 | |||
GasIdentityBase int64 = 1 | |||
) |
@ -1,7 +0,0 @@ | |||
package vm | |||
import ( | |||
"github.com/tendermint/go-logger" | |||
) | |||
var log = logger.New("module", "vm") |
@ -1,105 +0,0 @@ | |||
package vm | |||
import ( | |||
"crypto/sha256" | |||
"code.google.com/p/go.crypto/ripemd160" | |||
. "github.com/tendermint/go-common" | |||
) | |||
var registeredNativeContracts = make(map[Word256]NativeContract) | |||
func RegisteredNativeContract(addr Word256) bool { | |||
_, ok := registeredNativeContracts[addr] | |||
return ok | |||
} | |||
func RegisterNativeContract(addr Word256, fn NativeContract) bool { | |||
_, exists := registeredNativeContracts[addr] | |||
if exists { | |||
return false | |||
} | |||
registeredNativeContracts[addr] = fn | |||
return true | |||
} | |||
func init() { | |||
registerNativeContracts() | |||
registerSNativeContracts() | |||
} | |||
func registerNativeContracts() { | |||
// registeredNativeContracts[Int64ToWord256(1)] = ecrecoverFunc | |||
registeredNativeContracts[Int64ToWord256(2)] = sha256Func | |||
registeredNativeContracts[Int64ToWord256(3)] = ripemd160Func | |||
registeredNativeContracts[Int64ToWord256(4)] = identityFunc | |||
} | |||
//----------------------------------------------------------------------------- | |||
type NativeContract func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) | |||
/* Removed due to C dependency | |||
func ecrecoverFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { | |||
// Deduct gas | |||
gasRequired := GasEcRecover | |||
if *gas < gasRequired { | |||
return nil, ErrInsufficientGas | |||
} else { | |||
*gas -= gasRequired | |||
} | |||
// Recover | |||
hash := input[:32] | |||
v := byte(input[32] - 27) // ignore input[33:64], v is small. | |||
sig := append(input[64:], v) | |||
recovered, err := secp256k1.RecoverPubkey(hash, sig) | |||
if err != nil { | |||
return nil, err | |||
} | |||
hashed := sha3.Sha3(recovered[1:]) | |||
return LeftPadBytes(hashed, 32), nil | |||
} | |||
*/ | |||
func sha256Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { | |||
// Deduct gas | |||
gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base | |||
if *gas < gasRequired { | |||
return nil, ErrInsufficientGas | |||
} else { | |||
*gas -= gasRequired | |||
} | |||
// Hash | |||
hasher := sha256.New() | |||
// CONTRACT: this does not err | |||
hasher.Write(input) | |||
return hasher.Sum(nil), nil | |||
} | |||
func ripemd160Func(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { | |||
// Deduct gas | |||
gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base | |||
if *gas < gasRequired { | |||
return nil, ErrInsufficientGas | |||
} else { | |||
*gas -= gasRequired | |||
} | |||
// Hash | |||
hasher := ripemd160.New() | |||
// CONTRACT: this does not err | |||
hasher.Write(input) | |||
return LeftPadBytes(hasher.Sum(nil), 32), nil | |||
} | |||
func identityFunc(appState AppState, caller *Account, input []byte, gas *int64) (output []byte, err error) { | |||
// Deduct gas | |||
gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase | |||
if *gas < gasRequired { | |||
return nil, ErrInsufficientGas | |||
} else { | |||
*gas -= gasRequired | |||
} | |||
// Return identity | |||
return input, nil | |||
} |
@ -1,354 +0,0 @@ | |||
package vm | |||
import ( | |||
"fmt" | |||
"gopkg.in/fatih/set.v0" | |||
) | |||
type OpCode byte | |||
const ( | |||
// Op codes | |||
// 0x0 range - arithmetic ops | |||
STOP OpCode = iota | |||
ADD | |||
MUL | |||
SUB | |||
DIV | |||
SDIV | |||
MOD | |||
SMOD | |||
ADDMOD | |||
MULMOD | |||
EXP | |||
SIGNEXTEND | |||
) | |||
const ( | |||
LT OpCode = iota + 0x10 | |||
GT | |||
SLT | |||
SGT | |||
EQ | |||
ISZERO | |||
AND | |||
OR | |||
XOR | |||
NOT | |||
BYTE | |||
SHA3 = 0x20 | |||
) | |||
const ( | |||
// 0x30 range - closure state | |||
ADDRESS OpCode = 0x30 + iota | |||
BALANCE | |||
ORIGIN | |||
CALLER | |||
CALLVALUE | |||
CALLDATALOAD | |||
CALLDATASIZE | |||
CALLDATACOPY | |||
CODESIZE | |||
CODECOPY | |||
GASPRICE_DEPRECATED | |||
EXTCODESIZE | |||
EXTCODECOPY | |||
) | |||
const ( | |||
// 0x40 range - block operations | |||
BLOCKHASH OpCode = 0x40 + iota | |||
COINBASE | |||
TIMESTAMP | |||
BLOCKHEIGHT | |||
DIFFICULTY_DEPRECATED | |||
GASLIMIT | |||
) | |||
const ( | |||
// 0x50 range - 'storage' and execution | |||
POP OpCode = 0x50 + iota | |||
MLOAD | |||
MSTORE | |||
MSTORE8 | |||
SLOAD | |||
SSTORE | |||
JUMP | |||
JUMPI | |||
PC | |||
MSIZE | |||
GAS | |||
JUMPDEST | |||
) | |||
const ( | |||
// 0x60 range | |||
PUSH1 OpCode = 0x60 + iota | |||
PUSH2 | |||
PUSH3 | |||
PUSH4 | |||
PUSH5 | |||
PUSH6 | |||
PUSH7 | |||
PUSH8 | |||
PUSH9 | |||
PUSH10 | |||
PUSH11 | |||
PUSH12 | |||
PUSH13 | |||
PUSH14 | |||
PUSH15 | |||
PUSH16 | |||
PUSH17 | |||
PUSH18 | |||
PUSH19 | |||
PUSH20 | |||
PUSH21 | |||
PUSH22 | |||
PUSH23 | |||
PUSH24 | |||
PUSH25 | |||
PUSH26 | |||
PUSH27 | |||
PUSH28 | |||
PUSH29 | |||
PUSH30 | |||
PUSH31 | |||
PUSH32 | |||
DUP1 | |||
DUP2 | |||
DUP3 | |||
DUP4 | |||
DUP5 | |||
DUP6 | |||
DUP7 | |||
DUP8 | |||
DUP9 | |||
DUP10 | |||
DUP11 | |||
DUP12 | |||
DUP13 | |||
DUP14 | |||
DUP15 | |||
DUP16 | |||
SWAP1 | |||
SWAP2 | |||
SWAP3 | |||
SWAP4 | |||
SWAP5 | |||
SWAP6 | |||
SWAP7 | |||
SWAP8 | |||
SWAP9 | |||
SWAP10 | |||
SWAP11 | |||
SWAP12 | |||
SWAP13 | |||
SWAP14 | |||
SWAP15 | |||
SWAP16 | |||
) | |||
const ( | |||
LOG0 OpCode = 0xa0 + iota | |||
LOG1 | |||
LOG2 | |||
LOG3 | |||
LOG4 | |||
) | |||
const ( | |||
// 0xf0 range - closures | |||
CREATE OpCode = 0xf0 + iota | |||
CALL | |||
CALLCODE | |||
RETURN | |||
// 0x70 range - other | |||
SUICIDE = 0xff | |||
) | |||
// Since the opcodes aren't all in order we can't use a regular slice | |||
var opCodeToString = map[OpCode]string{ | |||
// 0x0 range - arithmetic ops | |||
STOP: "STOP", | |||
ADD: "ADD", | |||
MUL: "MUL", | |||
SUB: "SUB", | |||
DIV: "DIV", | |||
SDIV: "SDIV", | |||
MOD: "MOD", | |||
SMOD: "SMOD", | |||
EXP: "EXP", | |||
NOT: "NOT", | |||
LT: "LT", | |||
GT: "GT", | |||
SLT: "SLT", | |||
SGT: "SGT", | |||
EQ: "EQ", | |||
ISZERO: "ISZERO", | |||
SIGNEXTEND: "SIGNEXTEND", | |||
// 0x10 range - bit ops | |||
AND: "AND", | |||
OR: "OR", | |||
XOR: "XOR", | |||
BYTE: "BYTE", | |||
ADDMOD: "ADDMOD", | |||
MULMOD: "MULMOD", | |||
// 0x20 range - crypto | |||
SHA3: "SHA3", | |||
// 0x30 range - closure state | |||
ADDRESS: "ADDRESS", | |||
BALANCE: "BALANCE", | |||
ORIGIN: "ORIGIN", | |||
CALLER: "CALLER", | |||
CALLVALUE: "CALLVALUE", | |||
CALLDATALOAD: "CALLDATALOAD", | |||
CALLDATASIZE: "CALLDATASIZE", | |||
CALLDATACOPY: "CALLDATACOPY", | |||
CODESIZE: "CODESIZE", | |||
CODECOPY: "CODECOPY", | |||
GASPRICE_DEPRECATED: "TXGASPRICE_DEPRECATED", | |||
// 0x40 range - block operations | |||
BLOCKHASH: "BLOCKHASH", | |||
COINBASE: "COINBASE", | |||
TIMESTAMP: "TIMESTAMP", | |||
BLOCKHEIGHT: "BLOCKHEIGHT", | |||
DIFFICULTY_DEPRECATED: "DIFFICULTY_DEPRECATED", | |||
GASLIMIT: "GASLIMIT", | |||
EXTCODESIZE: "EXTCODESIZE", | |||
EXTCODECOPY: "EXTCODECOPY", | |||
// 0x50 range - 'storage' and execution | |||
POP: "POP", | |||
//DUP: "DUP", | |||
//SWAP: "SWAP", | |||
MLOAD: "MLOAD", | |||
MSTORE: "MSTORE", | |||
MSTORE8: "MSTORE8", | |||
SLOAD: "SLOAD", | |||
SSTORE: "SSTORE", | |||
JUMP: "JUMP", | |||
JUMPI: "JUMPI", | |||
PC: "PC", | |||
MSIZE: "MSIZE", | |||
GAS: "GAS", | |||
JUMPDEST: "JUMPDEST", | |||
// 0x60 range - push | |||
PUSH1: "PUSH1", | |||
PUSH2: "PUSH2", | |||
PUSH3: "PUSH3", | |||
PUSH4: "PUSH4", | |||
PUSH5: "PUSH5", | |||
PUSH6: "PUSH6", | |||
PUSH7: "PUSH7", | |||
PUSH8: "PUSH8", | |||
PUSH9: "PUSH9", | |||
PUSH10: "PUSH10", | |||
PUSH11: "PUSH11", | |||
PUSH12: "PUSH12", | |||
PUSH13: "PUSH13", | |||
PUSH14: "PUSH14", | |||
PUSH15: "PUSH15", | |||
PUSH16: "PUSH16", | |||
PUSH17: "PUSH17", | |||
PUSH18: "PUSH18", | |||
PUSH19: "PUSH19", | |||
PUSH20: "PUSH20", | |||
PUSH21: "PUSH21", | |||
PUSH22: "PUSH22", | |||
PUSH23: "PUSH23", | |||
PUSH24: "PUSH24", | |||
PUSH25: "PUSH25", | |||
PUSH26: "PUSH26", | |||
PUSH27: "PUSH27", | |||
PUSH28: "PUSH28", | |||
PUSH29: "PUSH29", | |||
PUSH30: "PUSH30", | |||
PUSH31: "PUSH31", | |||
PUSH32: "PUSH32", | |||
DUP1: "DUP1", | |||
DUP2: "DUP2", | |||
DUP3: "DUP3", | |||
DUP4: "DUP4", | |||
DUP5: "DUP5", | |||
DUP6: "DUP6", | |||
DUP7: "DUP7", | |||
DUP8: "DUP8", | |||
DUP9: "DUP9", | |||
DUP10: "DUP10", | |||
DUP11: "DUP11", | |||
DUP12: "DUP12", | |||
DUP13: "DUP13", | |||
DUP14: "DUP14", | |||
DUP15: "DUP15", | |||
DUP16: "DUP16", | |||
SWAP1: "SWAP1", | |||
SWAP2: "SWAP2", | |||
SWAP3: "SWAP3", | |||
SWAP4: "SWAP4", | |||
SWAP5: "SWAP5", | |||
SWAP6: "SWAP6", | |||
SWAP7: "SWAP7", | |||
SWAP8: "SWAP8", | |||
SWAP9: "SWAP9", | |||
SWAP10: "SWAP10", | |||
SWAP11: "SWAP11", | |||
SWAP12: "SWAP12", | |||
SWAP13: "SWAP13", | |||
SWAP14: "SWAP14", | |||
SWAP15: "SWAP15", | |||
SWAP16: "SWAP16", | |||
LOG0: "LOG0", | |||
LOG1: "LOG1", | |||
LOG2: "LOG2", | |||
LOG3: "LOG3", | |||
LOG4: "LOG4", | |||
// 0xf0 range | |||
CREATE: "CREATE", | |||
CALL: "CALL", | |||
RETURN: "RETURN", | |||
CALLCODE: "CALLCODE", | |||
// 0x70 range - other | |||
SUICIDE: "SUICIDE", | |||
} | |||
func (o OpCode) String() string { | |||
str := opCodeToString[o] | |||
if len(str) == 0 { | |||
return fmt.Sprintf("Missing opcode 0x%x", int(o)) | |||
} | |||
return str | |||
} | |||
//----------------------------------------------------------------------------- | |||
func AnalyzeJumpDests(code []byte) (dests *set.Set) { | |||
dests = set.New() | |||
for pc := uint64(0); pc < uint64(len(code)); pc++ { | |||
var op OpCode = OpCode(code[pc]) | |||
switch op { | |||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: | |||
a := uint64(op) - uint64(PUSH1) + 1 | |||
pc += a | |||
case JUMPDEST: | |||
dests.Add(pc) | |||
} | |||
} | |||
return | |||
} |
@ -1,35 +0,0 @@ | |||
package randentropy | |||
import ( | |||
crand "crypto/rand" | |||
"github.com/tendermint/tendermint/vm/sha3" | |||
"io" | |||
) | |||
var Reader io.Reader = &randEntropy{} | |||
type randEntropy struct { | |||
} | |||
func (*randEntropy) Read(bytes []byte) (n int, err error) { | |||
readBytes := GetEntropyCSPRNG(len(bytes)) | |||
copy(bytes, readBytes) | |||
return len(bytes), nil | |||
} | |||
// TODO: copied from crypto.go , move to sha3 package? | |||
func Sha3(data []byte) []byte { | |||
d := sha3.NewKeccak256() | |||
d.Write(data) | |||
return d.Sum(nil) | |||
} | |||
func GetEntropyCSPRNG(n int) []byte { | |||
mainBuff := make([]byte, n) | |||
_, err := io.ReadFull(crand.Reader, mainBuff) | |||
if err != nil { | |||
panic("reading from crypto/rand failed: " + err.Error()) | |||
} | |||
return mainBuff | |||
} |
@ -1,171 +0,0 @@ | |||
// Copyright 2013 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package sha3 | |||
// This file implements the core Keccak permutation function necessary for computing SHA3. | |||
// This is implemented in a separate file to allow for replacement by an optimized implementation. | |||
// Nothing in this package is exported. | |||
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/). | |||
// rc stores the round constants for use in the ι step. | |||
var rc = [...]uint64{ | |||
0x0000000000000001, | |||
0x0000000000008082, | |||
0x800000000000808A, | |||
0x8000000080008000, | |||
0x000000000000808B, | |||
0x0000000080000001, | |||
0x8000000080008081, | |||
0x8000000000008009, | |||
0x000000000000008A, | |||
0x0000000000000088, | |||
0x0000000080008009, | |||
0x000000008000000A, | |||
0x000000008000808B, | |||
0x800000000000008B, | |||
0x8000000000008089, | |||
0x8000000000008003, | |||
0x8000000000008002, | |||
0x8000000000000080, | |||
0x000000000000800A, | |||
0x800000008000000A, | |||
0x8000000080008081, | |||
0x8000000000008080, | |||
0x0000000080000001, | |||
0x8000000080008008, | |||
} | |||
// ro_xx represent the rotation offsets for use in the χ step. | |||
// Defining them as const instead of in an array allows the compiler to insert constant shifts. | |||
const ( | |||
ro_00 = 0 | |||
ro_01 = 36 | |||
ro_02 = 3 | |||
ro_03 = 41 | |||
ro_04 = 18 | |||
ro_05 = 1 | |||
ro_06 = 44 | |||
ro_07 = 10 | |||
ro_08 = 45 | |||
ro_09 = 2 | |||
ro_10 = 62 | |||
ro_11 = 6 | |||
ro_12 = 43 | |||
ro_13 = 15 | |||
ro_14 = 61 | |||
ro_15 = 28 | |||
ro_16 = 55 | |||
ro_17 = 25 | |||
ro_18 = 21 | |||
ro_19 = 56 | |||
ro_20 = 27 | |||
ro_21 = 20 | |||
ro_22 = 39 | |||
ro_23 = 8 | |||
ro_24 = 14 | |||
) | |||
// keccakF computes the complete Keccak-f function consisting of 24 rounds with a different | |||
// constant (rc) in each round. This implementation fully unrolls the round function to avoid | |||
// inner loops, as well as pre-calculating shift offsets. | |||
func (d *digest) keccakF() { | |||
for _, roundConstant := range rc { | |||
// θ step | |||
d.c[0] = d.a[0] ^ d.a[5] ^ d.a[10] ^ d.a[15] ^ d.a[20] | |||
d.c[1] = d.a[1] ^ d.a[6] ^ d.a[11] ^ d.a[16] ^ d.a[21] | |||
d.c[2] = d.a[2] ^ d.a[7] ^ d.a[12] ^ d.a[17] ^ d.a[22] | |||
d.c[3] = d.a[3] ^ d.a[8] ^ d.a[13] ^ d.a[18] ^ d.a[23] | |||
d.c[4] = d.a[4] ^ d.a[9] ^ d.a[14] ^ d.a[19] ^ d.a[24] | |||
d.d[0] = d.c[4] ^ (d.c[1]<<1 ^ d.c[1]>>63) | |||
d.d[1] = d.c[0] ^ (d.c[2]<<1 ^ d.c[2]>>63) | |||
d.d[2] = d.c[1] ^ (d.c[3]<<1 ^ d.c[3]>>63) | |||
d.d[3] = d.c[2] ^ (d.c[4]<<1 ^ d.c[4]>>63) | |||
d.d[4] = d.c[3] ^ (d.c[0]<<1 ^ d.c[0]>>63) | |||
d.a[0] ^= d.d[0] | |||
d.a[1] ^= d.d[1] | |||
d.a[2] ^= d.d[2] | |||
d.a[3] ^= d.d[3] | |||
d.a[4] ^= d.d[4] | |||
d.a[5] ^= d.d[0] | |||
d.a[6] ^= d.d[1] | |||
d.a[7] ^= d.d[2] | |||
d.a[8] ^= d.d[3] | |||
d.a[9] ^= d.d[4] | |||
d.a[10] ^= d.d[0] | |||
d.a[11] ^= d.d[1] | |||
d.a[12] ^= d.d[2] | |||
d.a[13] ^= d.d[3] | |||
d.a[14] ^= d.d[4] | |||
d.a[15] ^= d.d[0] | |||
d.a[16] ^= d.d[1] | |||
d.a[17] ^= d.d[2] | |||
d.a[18] ^= d.d[3] | |||
d.a[19] ^= d.d[4] | |||
d.a[20] ^= d.d[0] | |||
d.a[21] ^= d.d[1] | |||
d.a[22] ^= d.d[2] | |||
d.a[23] ^= d.d[3] | |||
d.a[24] ^= d.d[4] | |||
// ρ and π steps | |||
d.b[0] = d.a[0] | |||
d.b[1] = d.a[6]<<ro_06 ^ d.a[6]>>(64-ro_06) | |||
d.b[2] = d.a[12]<<ro_12 ^ d.a[12]>>(64-ro_12) | |||
d.b[3] = d.a[18]<<ro_18 ^ d.a[18]>>(64-ro_18) | |||
d.b[4] = d.a[24]<<ro_24 ^ d.a[24]>>(64-ro_24) | |||
d.b[5] = d.a[3]<<ro_15 ^ d.a[3]>>(64-ro_15) | |||
d.b[6] = d.a[9]<<ro_21 ^ d.a[9]>>(64-ro_21) | |||
d.b[7] = d.a[10]<<ro_02 ^ d.a[10]>>(64-ro_02) | |||
d.b[8] = d.a[16]<<ro_08 ^ d.a[16]>>(64-ro_08) | |||
d.b[9] = d.a[22]<<ro_14 ^ d.a[22]>>(64-ro_14) | |||
d.b[10] = d.a[1]<<ro_05 ^ d.a[1]>>(64-ro_05) | |||
d.b[11] = d.a[7]<<ro_11 ^ d.a[7]>>(64-ro_11) | |||
d.b[12] = d.a[13]<<ro_17 ^ d.a[13]>>(64-ro_17) | |||
d.b[13] = d.a[19]<<ro_23 ^ d.a[19]>>(64-ro_23) | |||
d.b[14] = d.a[20]<<ro_04 ^ d.a[20]>>(64-ro_04) | |||
d.b[15] = d.a[4]<<ro_20 ^ d.a[4]>>(64-ro_20) | |||
d.b[16] = d.a[5]<<ro_01 ^ d.a[5]>>(64-ro_01) | |||
d.b[17] = d.a[11]<<ro_07 ^ d.a[11]>>(64-ro_07) | |||
d.b[18] = d.a[17]<<ro_13 ^ d.a[17]>>(64-ro_13) | |||
d.b[19] = d.a[23]<<ro_19 ^ d.a[23]>>(64-ro_19) | |||
d.b[20] = d.a[2]<<ro_10 ^ d.a[2]>>(64-ro_10) | |||
d.b[21] = d.a[8]<<ro_16 ^ d.a[8]>>(64-ro_16) | |||
d.b[22] = d.a[14]<<ro_22 ^ d.a[14]>>(64-ro_22) | |||
d.b[23] = d.a[15]<<ro_03 ^ d.a[15]>>(64-ro_03) | |||
d.b[24] = d.a[21]<<ro_09 ^ d.a[21]>>(64-ro_09) | |||
// χ step | |||
d.a[0] = d.b[0] ^ (^d.b[1] & d.b[2]) | |||
d.a[1] = d.b[1] ^ (^d.b[2] & d.b[3]) | |||
d.a[2] = d.b[2] ^ (^d.b[3] & d.b[4]) | |||
d.a[3] = d.b[3] ^ (^d.b[4] & d.b[0]) | |||
d.a[4] = d.b[4] ^ (^d.b[0] & d.b[1]) | |||
d.a[5] = d.b[5] ^ (^d.b[6] & d.b[7]) | |||
d.a[6] = d.b[6] ^ (^d.b[7] & d.b[8]) | |||
d.a[7] = d.b[7] ^ (^d.b[8] & d.b[9]) | |||
d.a[8] = d.b[8] ^ (^d.b[9] & d.b[5]) | |||
d.a[9] = d.b[9] ^ (^d.b[5] & d.b[6]) | |||
d.a[10] = d.b[10] ^ (^d.b[11] & d.b[12]) | |||
d.a[11] = d.b[11] ^ (^d.b[12] & d.b[13]) | |||
d.a[12] = d.b[12] ^ (^d.b[13] & d.b[14]) | |||
d.a[13] = d.b[13] ^ (^d.b[14] & d.b[10]) | |||
d.a[14] = d.b[14] ^ (^d.b[10] & d.b[11]) | |||
d.a[15] = d.b[15] ^ (^d.b[16] & d.b[17]) | |||
d.a[16] = d.b[16] ^ (^d.b[17] & d.b[18]) | |||
d.a[17] = d.b[17] ^ (^d.b[18] & d.b[19]) | |||
d.a[18] = d.b[18] ^ (^d.b[19] & d.b[15]) | |||
d.a[19] = d.b[19] ^ (^d.b[15] & d.b[16]) | |||
d.a[20] = d.b[20] ^ (^d.b[21] & d.b[22]) | |||
d.a[21] = d.b[21] ^ (^d.b[22] & d.b[23]) | |||
d.a[22] = d.b[22] ^ (^d.b[23] & d.b[24]) | |||
d.a[23] = d.b[23] ^ (^d.b[24] & d.b[20]) | |||
d.a[24] = d.b[24] ^ (^d.b[20] & d.b[21]) | |||
// ι step | |||
d.a[0] ^= roundConstant | |||
} | |||
} |
@ -1,224 +0,0 @@ | |||
// Copyright 2013 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Package sha3 implements the SHA3 hash algorithm (formerly called Keccak) chosen by NIST in 2012. | |||
// This file provides a SHA3 implementation which implements the standard hash.Hash interface. | |||
// Writing input data, including padding, and reading output data are computed in this file. | |||
// Note that the current implementation can compute the hash of an integral number of bytes only. | |||
// This is a consequence of the hash interface in which a buffer of bytes is passed in. | |||
// The internals of the Keccak-f function are computed in keccakf.go. | |||
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/). | |||
package sha3 | |||
import ( | |||
"encoding/binary" | |||
"hash" | |||
) | |||
// laneSize is the size in bytes of each "lane" of the internal state of SHA3 (5 * 5 * 8). | |||
// Note that changing this size would requires using a type other than uint64 to store each lane. | |||
const laneSize = 8 | |||
// sliceSize represents the dimensions of the internal state, a square matrix of | |||
// sliceSize ** 2 lanes. This is the size of both the "rows" and "columns" dimensions in the | |||
// terminology of the SHA3 specification. | |||
const sliceSize = 5 | |||
// numLanes represents the total number of lanes in the state. | |||
const numLanes = sliceSize * sliceSize | |||
// stateSize is the size in bytes of the internal state of SHA3 (5 * 5 * WSize). | |||
const stateSize = laneSize * numLanes | |||
// digest represents the partial evaluation of a checksum. | |||
// Note that capacity, and not outputSize, is the critical security parameter, as SHA3 can output | |||
// an arbitrary number of bytes for any given capacity. The Keccak proposal recommends that | |||
// capacity = 2*outputSize to ensure that finding a collision of size outputSize requires | |||
// O(2^{outputSize/2}) computations (the birthday lower bound). Future standards may modify the | |||
// capacity/outputSize ratio to allow for more output with lower cryptographic security. | |||
type digest struct { | |||
a [numLanes]uint64 // main state of the hash | |||
b [numLanes]uint64 // intermediate states | |||
c [sliceSize]uint64 // intermediate states | |||
d [sliceSize]uint64 // intermediate states | |||
outputSize int // desired output size in bytes | |||
capacity int // number of bytes to leave untouched during squeeze/absorb | |||
absorbed int // number of bytes absorbed thus far | |||
} | |||
// minInt returns the lesser of two integer arguments, to simplify the absorption routine. | |||
func minInt(v1, v2 int) int { | |||
if v1 <= v2 { | |||
return v1 | |||
} | |||
return v2 | |||
} | |||
// rate returns the number of bytes of the internal state which can be absorbed or squeezed | |||
// in between calls to the permutation function. | |||
func (d *digest) rate() int { | |||
return stateSize - d.capacity | |||
} | |||
// Reset clears the internal state by zeroing bytes in the state buffer. | |||
// This can be skipped for a newly-created hash state; the default zero-allocated state is correct. | |||
func (d *digest) Reset() { | |||
d.absorbed = 0 | |||
for i := range d.a { | |||
d.a[i] = 0 | |||
} | |||
} | |||
// BlockSize, required by the hash.Hash interface, does not have a standard intepretation | |||
// for a sponge-based construction like SHA3. We return the data rate: the number of bytes which | |||
// can be absorbed per invocation of the permutation function. For Merkle-Damgård based hashes | |||
// (ie SHA1, SHA2, MD5) the output size of the internal compression function is returned. | |||
// We consider this to be roughly equivalent because it represents the number of bytes of output | |||
// produced per cryptographic operation. | |||
func (d *digest) BlockSize() int { return d.rate() } | |||
// Size returns the output size of the hash function in bytes. | |||
func (d *digest) Size() int { | |||
return d.outputSize | |||
} | |||
// unalignedAbsorb is a helper function for Write, which absorbs data that isn't aligned with an | |||
// 8-byte lane. This requires shifting the individual bytes into position in a uint64. | |||
func (d *digest) unalignedAbsorb(p []byte) { | |||
var t uint64 | |||
for i := len(p) - 1; i >= 0; i-- { | |||
t <<= 8 | |||
t |= uint64(p[i]) | |||
} | |||
offset := (d.absorbed) % d.rate() | |||
t <<= 8 * uint(offset%laneSize) | |||
d.a[offset/laneSize] ^= t | |||
d.absorbed += len(p) | |||
} | |||
// Write "absorbs" bytes into the state of the SHA3 hash, updating as needed when the sponge | |||
// "fills up" with rate() bytes. Since lanes are stored internally as type uint64, this requires | |||
// converting the incoming bytes into uint64s using a little endian interpretation. This | |||
// implementation is optimized for large, aligned writes of multiples of 8 bytes (laneSize). | |||
// Non-aligned or uneven numbers of bytes require shifting and are slower. | |||
func (d *digest) Write(p []byte) (int, error) { | |||
// An initial offset is needed if the we aren't absorbing to the first lane initially. | |||
offset := d.absorbed % d.rate() | |||
toWrite := len(p) | |||
// The first lane may need to absorb unaligned and/or incomplete data. | |||
if (offset%laneSize != 0 || len(p) < 8) && len(p) > 0 { | |||
toAbsorb := minInt(laneSize-(offset%laneSize), len(p)) | |||
d.unalignedAbsorb(p[:toAbsorb]) | |||
p = p[toAbsorb:] | |||
offset = (d.absorbed) % d.rate() | |||
// For every rate() bytes absorbed, the state must be permuted via the F Function. | |||
if (d.absorbed)%d.rate() == 0 { | |||
d.keccakF() | |||
} | |||
} | |||
// This loop should absorb the bulk of the data into full, aligned lanes. | |||
// It will call the update function as necessary. | |||
for len(p) > 7 { | |||
firstLane := offset / laneSize | |||
lastLane := minInt(d.rate()/laneSize, firstLane+len(p)/laneSize) | |||
// This inner loop absorbs input bytes into the state in groups of 8, converted to uint64s. | |||
for lane := firstLane; lane < lastLane; lane++ { | |||
d.a[lane] ^= binary.LittleEndian.Uint64(p[:laneSize]) | |||
p = p[laneSize:] | |||
} | |||
d.absorbed += (lastLane - firstLane) * laneSize | |||
// For every rate() bytes absorbed, the state must be permuted via the F Function. | |||
if (d.absorbed)%d.rate() == 0 { | |||
d.keccakF() | |||
} | |||
offset = 0 | |||
} | |||
// If there are insufficient bytes to fill the final lane, an unaligned absorption. | |||
// This should always start at a correct lane boundary though, or else it would be caught | |||
// by the uneven opening lane case above. | |||
if len(p) > 0 { | |||
d.unalignedAbsorb(p) | |||
} | |||
return toWrite, nil | |||
} | |||
// pad computes the SHA3 padding scheme based on the number of bytes absorbed. | |||
// The padding is a 1 bit, followed by an arbitrary number of 0s and then a final 1 bit, such that | |||
// the input bits plus padding bits are a multiple of rate(). Adding the padding simply requires | |||
// xoring an opening and closing bit into the appropriate lanes. | |||
func (d *digest) pad() { | |||
offset := d.absorbed % d.rate() | |||
// The opening pad bit must be shifted into position based on the number of bytes absorbed | |||
padOpenLane := offset / laneSize | |||
d.a[padOpenLane] ^= 0x0000000000000001 << uint(8*(offset%laneSize)) | |||
// The closing padding bit is always in the last position | |||
padCloseLane := (d.rate() / laneSize) - 1 | |||
d.a[padCloseLane] ^= 0x8000000000000000 | |||
} | |||
// finalize prepares the hash to output data by padding and one final permutation of the state. | |||
func (d *digest) finalize() { | |||
d.pad() | |||
d.keccakF() | |||
} | |||
// squeeze outputs an arbitrary number of bytes from the hash state. | |||
// Squeezing can require multiple calls to the F function (one per rate() bytes squeezed), | |||
// although this is not the case for standard SHA3 parameters. This implementation only supports | |||
// squeezing a single time, subsequent squeezes may lose alignment. Future implementations | |||
// may wish to support multiple squeeze calls, for example to support use as a PRNG. | |||
func (d *digest) squeeze(in []byte, toSqueeze int) []byte { | |||
// Because we read in blocks of laneSize, we need enough room to read | |||
// an integral number of lanes | |||
needed := toSqueeze + (laneSize-toSqueeze%laneSize)%laneSize | |||
if cap(in)-len(in) < needed { | |||
newIn := make([]byte, len(in), len(in)+needed) | |||
copy(newIn, in) | |||
in = newIn | |||
} | |||
out := in[len(in) : len(in)+needed] | |||
for len(out) > 0 { | |||
for i := 0; i < d.rate() && len(out) > 0; i += laneSize { | |||
binary.LittleEndian.PutUint64(out[:], d.a[i/laneSize]) | |||
out = out[laneSize:] | |||
} | |||
if len(out) > 0 { | |||
d.keccakF() | |||
} | |||
} | |||
return in[:len(in)+toSqueeze] // Re-slice in case we wrote extra data. | |||
} | |||
// Sum applies padding to the hash state and then squeezes out the desired nubmer of output bytes. | |||
func (d *digest) Sum(in []byte) []byte { | |||
// Make a copy of the original hash so that caller can keep writing and summing. | |||
dup := *d | |||
dup.finalize() | |||
return dup.squeeze(in, dup.outputSize) | |||
} | |||
// The NewKeccakX constructors enable initializing a hash in any of the four recommend sizes | |||
// from the Keccak specification, all of which set capacity=2*outputSize. Note that the final | |||
// NIST standard for SHA3 may specify different input/output lengths. | |||
// The output size is indicated in bits but converted into bytes internally. | |||
func NewKeccak224() hash.Hash { return &digest{outputSize: 224 / 8, capacity: 2 * 224 / 8} } | |||
func NewKeccak256() hash.Hash { return &digest{outputSize: 256 / 8, capacity: 2 * 256 / 8} } | |||
func NewKeccak384() hash.Hash { return &digest{outputSize: 384 / 8, capacity: 2 * 384 / 8} } | |||
func NewKeccak512() hash.Hash { return &digest{outputSize: 512 / 8, capacity: 2 * 512 / 8} } | |||
func Sha3(data ...[]byte) []byte { | |||
d := NewKeccak256() | |||
for _, b := range data { | |||
d.Write(b) | |||
} | |||
return d.Sum(nil) | |||
} |
@ -1,233 +0,0 @@ | |||
package vm | |||
import ( | |||
"fmt" | |||
. "github.com/tendermint/go-common" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
) | |||
// TODO: ABI | |||
//------------------------------------------------------------------------------------------------ | |||
// Registered SNative contracts | |||
func registerSNativeContracts() { | |||
registeredNativeContracts[LeftPadWord256([]byte("has_base"))] = hasBasePerm | |||
registeredNativeContracts[LeftPadWord256([]byte("set_base"))] = setBasePerm | |||
registeredNativeContracts[LeftPadWord256([]byte("unset_base"))] = unsetBasePerm | |||
registeredNativeContracts[LeftPadWord256([]byte("set_global"))] = setGlobalPerm | |||
registeredNativeContracts[LeftPadWord256([]byte("has_role"))] = hasRole | |||
registeredNativeContracts[LeftPadWord256([]byte("add_role"))] = addRole | |||
registeredNativeContracts[LeftPadWord256([]byte("rm_role"))] = rmRole | |||
} | |||
//----------------------------------------------------------------------------- | |||
// snative are native contracts that can access and modify an account's permissions | |||
// TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) | |||
func hasBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.HasBase) { | |||
return nil, ErrInvalidPermission{caller.Address, "has_base"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permFlag)") | |||
} | |||
addr, permNum := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
permN := ptypes.PermFlag(Uint64FromWord256(permNum)) // already shifted | |||
if !ValidPermN(permN) { | |||
return nil, ptypes.ErrInvalidPermission(permN) | |||
} | |||
var permInt byte | |||
if HasPermission(appState, vmAcc, permN) { | |||
permInt = 0x1 | |||
} else { | |||
permInt = 0x0 | |||
} | |||
dbg.Printf("snative.hasBasePerm(0x%X, %b) = %v\n", addr.Postfix(20), permN, permInt) | |||
return LeftPadWord256([]byte{permInt}).Bytes(), nil | |||
} | |||
func setBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.SetBase) { | |||
return nil, ErrInvalidPermission{caller.Address, "set_base"} | |||
} | |||
if len(args) != 3*32 { | |||
return nil, fmt.Errorf("setBase() takes three arguments (address, permFlag, permission value)") | |||
} | |||
addr, permNum, perm := returnThreeArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
permN := ptypes.PermFlag(Uint64FromWord256(permNum)) | |||
if !ValidPermN(permN) { | |||
return nil, ptypes.ErrInvalidPermission(permN) | |||
} | |||
permV := !perm.IsZero() | |||
if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { | |||
return nil, err | |||
} | |||
appState.UpdateAccount(vmAcc) | |||
dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV) | |||
return perm.Bytes(), nil | |||
} | |||
func unsetBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.UnsetBase) { | |||
return nil, ErrInvalidPermission{caller.Address, "unset_base"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("unsetBase() takes two arguments (address, permFlag)") | |||
} | |||
addr, permNum := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
permN := ptypes.PermFlag(Uint64FromWord256(permNum)) | |||
if !ValidPermN(permN) { | |||
return nil, ptypes.ErrInvalidPermission(permN) | |||
} | |||
if err = vmAcc.Permissions.Base.Unset(permN); err != nil { | |||
return nil, err | |||
} | |||
appState.UpdateAccount(vmAcc) | |||
dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN) | |||
return permNum.Bytes(), nil | |||
} | |||
func setGlobalPerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.SetGlobal) { | |||
return nil, ErrInvalidPermission{caller.Address, "set_global"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("setGlobal() takes two arguments (permFlag, permission value)") | |||
} | |||
permNum, perm := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) | |||
if vmAcc == nil { | |||
PanicSanity("cant find the global permissions account") | |||
} | |||
permN := ptypes.PermFlag(Uint64FromWord256(permNum)) | |||
if !ValidPermN(permN) { | |||
return nil, ptypes.ErrInvalidPermission(permN) | |||
} | |||
permV := !perm.IsZero() | |||
if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { | |||
return nil, err | |||
} | |||
appState.UpdateAccount(vmAcc) | |||
dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV) | |||
return perm.Bytes(), nil | |||
} | |||
func hasRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.HasRole) { | |||
return nil, ErrInvalidPermission{caller.Address, "has_role"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") | |||
} | |||
addr, role := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
roleS := string(role.Bytes()) | |||
var permInt byte | |||
if vmAcc.Permissions.HasRole(roleS) { | |||
permInt = 0x1 | |||
} else { | |||
permInt = 0x0 | |||
} | |||
dbg.Printf("snative.hasRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) | |||
return LeftPadWord256([]byte{permInt}).Bytes(), nil | |||
} | |||
func addRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.AddRole) { | |||
return nil, ErrInvalidPermission{caller.Address, "add_role"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("addRole() takes two arguments (address, role)") | |||
} | |||
addr, role := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
roleS := string(role.Bytes()) | |||
var permInt byte | |||
if vmAcc.Permissions.AddRole(roleS) { | |||
permInt = 0x1 | |||
} else { | |||
permInt = 0x0 | |||
} | |||
appState.UpdateAccount(vmAcc) | |||
dbg.Printf("snative.addRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) | |||
return LeftPadWord256([]byte{permInt}).Bytes(), nil | |||
} | |||
func rmRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { | |||
if !HasPermission(appState, caller, ptypes.RmRole) { | |||
return nil, ErrInvalidPermission{caller.Address, "rm_role"} | |||
} | |||
if len(args) != 2*32 { | |||
return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") | |||
} | |||
addr, role := returnTwoArgs(args) | |||
vmAcc := appState.GetAccount(addr) | |||
if vmAcc == nil { | |||
return nil, fmt.Errorf("Unknown account %X", addr) | |||
} | |||
roleS := string(role.Bytes()) | |||
var permInt byte | |||
if vmAcc.Permissions.RmRole(roleS) { | |||
permInt = 0x1 | |||
} else { | |||
permInt = 0x0 | |||
} | |||
appState.UpdateAccount(vmAcc) | |||
dbg.Printf("snative.rmRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) | |||
return LeftPadWord256([]byte{permInt}).Bytes(), nil | |||
} | |||
//------------------------------------------------------------------------------------------------ | |||
// Errors and utility funcs | |||
type ErrInvalidPermission struct { | |||
Address Word256 | |||
SNative string | |||
} | |||
func (e ErrInvalidPermission) Error() string { | |||
return fmt.Sprintf("Account %X does not have permission snative.%s", e.Address.Postfix(20), e.SNative) | |||
} | |||
// Checks if a permission flag is valid (a known base chain or snative permission) | |||
func ValidPermN(n ptypes.PermFlag) bool { | |||
if n > ptypes.TopPermFlag { | |||
return false | |||
} | |||
return true | |||
} | |||
// CONTRACT: length has already been checked | |||
func returnTwoArgs(args []byte) (a Word256, b Word256) { | |||
copy(a[:], args[:32]) | |||
copy(b[:], args[32:64]) | |||
return | |||
} | |||
// CONTRACT: length has already been checked | |||
func returnThreeArgs(args []byte) (a Word256, b Word256, c Word256) { | |||
copy(a[:], args[:32]) | |||
copy(b[:], args[32:64]) | |||
copy(c[:], args[64:96]) | |||
return | |||
} |
@ -1,126 +0,0 @@ | |||
package vm | |||
import ( | |||
"fmt" | |||
. "github.com/tendermint/go-common" | |||
) | |||
// Not goroutine safe | |||
type Stack struct { | |||
data []Word256 | |||
ptr int | |||
gas *int64 | |||
err *error | |||
} | |||
func NewStack(capacity int, gas *int64, err *error) *Stack { | |||
return &Stack{ | |||
data: make([]Word256, capacity), | |||
ptr: 0, | |||
gas: gas, | |||
err: err, | |||
} | |||
} | |||
func (st *Stack) useGas(gasToUse int64) { | |||
if *st.gas > gasToUse { | |||
*st.gas -= gasToUse | |||
} else { | |||
st.setErr(ErrInsufficientGas) | |||
} | |||
} | |||
func (st *Stack) setErr(err error) { | |||
if *st.err == nil { | |||
*st.err = err | |||
} | |||
} | |||
func (st *Stack) Push(d Word256) { | |||
st.useGas(GasStackOp) | |||
if st.ptr == cap(st.data) { | |||
st.setErr(ErrDataStackOverflow) | |||
return | |||
} | |||
st.data[st.ptr] = d | |||
st.ptr++ | |||
} | |||
// currently only called after Sha3 | |||
func (st *Stack) PushBytes(bz []byte) { | |||
if len(bz) != 32 { | |||
PanicSanity("Invalid bytes size: expected 32") | |||
} | |||
st.Push(LeftPadWord256(bz)) | |||
} | |||
func (st *Stack) Push64(i int64) { | |||
st.Push(Int64ToWord256(i)) | |||
} | |||
func (st *Stack) Pop() Word256 { | |||
st.useGas(GasStackOp) | |||
if st.ptr == 0 { | |||
st.setErr(ErrDataStackUnderflow) | |||
return Zero256 | |||
} | |||
st.ptr-- | |||
return st.data[st.ptr] | |||
} | |||
func (st *Stack) PopBytes() []byte { | |||
return st.Pop().Bytes() | |||
} | |||
func (st *Stack) Pop64() int64 { | |||
d := st.Pop() | |||
return Int64FromWord256(d) | |||
} | |||
func (st *Stack) Len() int { | |||
return st.ptr | |||
} | |||
func (st *Stack) Swap(n int) { | |||
st.useGas(GasStackOp) | |||
if st.ptr < n { | |||
st.setErr(ErrDataStackUnderflow) | |||
return | |||
} | |||
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n] | |||
return | |||
} | |||
func (st *Stack) Dup(n int) { | |||
st.useGas(GasStackOp) | |||
if st.ptr < n { | |||
st.setErr(ErrDataStackUnderflow) | |||
return | |||
} | |||
st.Push(st.data[st.ptr-n]) | |||
return | |||
} | |||
// Not an opcode, costs no gas. | |||
func (st *Stack) Peek() Word256 { | |||
if st.ptr == 0 { | |||
st.setErr(ErrDataStackUnderflow) | |||
return Zero256 | |||
} | |||
return st.data[st.ptr-1] | |||
} | |||
func (st *Stack) Print(n int) { | |||
fmt.Println("### stack ###") | |||
if st.ptr > 0 { | |||
nn := MinInt(n, st.ptr) | |||
for j, i := 0, st.ptr-1; i > st.ptr-1-nn; i-- { | |||
fmt.Printf("%-3d %X\n", j, st.data[i]) | |||
j += 1 | |||
} | |||
} else { | |||
fmt.Println("-- empty --") | |||
} | |||
fmt.Println("#############") | |||
} |
@ -1,79 +0,0 @@ | |||
package vm | |||
import ( | |||
. "github.com/tendermint/go-common" | |||
. "github.com/tendermint/tendermint/vm" | |||
"github.com/tendermint/tendermint/vm/sha3" | |||
) | |||
type FakeAppState struct { | |||
accounts map[string]*Account | |||
storage map[string]Word256 | |||
} | |||
func (fas *FakeAppState) GetAccount(addr Word256) *Account { | |||
account := fas.accounts[addr.String()] | |||
return account | |||
} | |||
func (fas *FakeAppState) UpdateAccount(account *Account) { | |||
fas.accounts[account.Address.String()] = account | |||
} | |||
func (fas *FakeAppState) RemoveAccount(account *Account) { | |||
_, ok := fas.accounts[account.Address.String()] | |||
if !ok { | |||
panic(Fmt("Invalid account addr: %X", account.Address)) | |||
} else { | |||
// Remove account | |||
delete(fas.accounts, account.Address.String()) | |||
} | |||
} | |||
func (fas *FakeAppState) CreateAccount(creator *Account) *Account { | |||
addr := createAddress(creator) | |||
account := fas.accounts[addr.String()] | |||
if account == nil { | |||
return &Account{ | |||
Address: addr, | |||
Balance: 0, | |||
Code: nil, | |||
Nonce: 0, | |||
} | |||
} else { | |||
panic(Fmt("Invalid account addr: %X", addr)) | |||
} | |||
} | |||
func (fas *FakeAppState) GetStorage(addr Word256, key Word256) Word256 { | |||
_, ok := fas.accounts[addr.String()] | |||
if !ok { | |||
panic(Fmt("Invalid account addr: %X", addr)) | |||
} | |||
value, ok := fas.storage[addr.String()+key.String()] | |||
if ok { | |||
return value | |||
} else { | |||
return Zero256 | |||
} | |||
} | |||
func (fas *FakeAppState) SetStorage(addr Word256, key Word256, value Word256) { | |||
_, ok := fas.accounts[addr.String()] | |||
if !ok { | |||
panic(Fmt("Invalid account addr: %X", addr)) | |||
} | |||
fas.storage[addr.String()+key.String()] = value | |||
} | |||
// Creates a 20 byte address and bumps the nonce. | |||
func createAddress(creator *Account) Word256 { | |||
nonce := creator.Nonce | |||
creator.Nonce += 1 | |||
temp := make([]byte, 32+8) | |||
copy(temp, creator.Address[:]) | |||
PutInt64BE(temp[32:], nonce) | |||
return LeftPadWord256(sha3.Sha3(temp)[:20]) | |||
} |
@ -1,90 +0,0 @@ | |||
package vm | |||
import ( | |||
"bytes" | |||
"reflect" | |||
"testing" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/events" | |||
"github.com/tendermint/tendermint/types" | |||
. "github.com/tendermint/tendermint/vm" | |||
) | |||
var expectedData = []byte{0x10} | |||
var expectedHeight int64 = 0 | |||
var expectedTopics = []Word256{ | |||
Int64ToWord256(1), | |||
Int64ToWord256(2), | |||
Int64ToWord256(3), | |||
Int64ToWord256(4)} | |||
// Tests logs and events. | |||
func TestLog4(t *testing.T) { | |||
st := newAppState() | |||
// Create accounts | |||
account1 := &Account{ | |||
Address: LeftPadWord256(makeBytes(20)), | |||
} | |||
account2 := &Account{ | |||
Address: LeftPadWord256(makeBytes(20)), | |||
} | |||
st.accounts[account1.Address.String()] = account1 | |||
st.accounts[account2.Address.String()] = account2 | |||
ourVm := NewVM(st, newParams(), Zero256, nil) | |||
eventSwitch := events.NewEventSwitch() | |||
_, err := eventSwitch.Start() | |||
if err != nil { | |||
t.Errorf("Failed to start eventSwitch: %v", err) | |||
} | |||
eventID := types.EventStringLogEvent(account2.Address.Postfix(20)) | |||
doneChan := make(chan struct{}, 1) | |||
eventSwitch.AddListenerForEvent("test", eventID, func(event types.EventData) { | |||
logEvent := event.(types.EventDataLog) | |||
// No need to test address as this event would not happen if it wasn't correct | |||
if !reflect.DeepEqual(logEvent.Topics, expectedTopics) { | |||
t.Errorf("Event topics are wrong. Got: %v. Expected: %v", logEvent.Topics, expectedTopics) | |||
} | |||
if !bytes.Equal(logEvent.Data, expectedData) { | |||
t.Errorf("Event data is wrong. Got: %s. Expected: %s", logEvent.Data, expectedData) | |||
} | |||
if logEvent.Height != expectedHeight { | |||
t.Errorf("Event block height is wrong. Got: %d. Expected: %d", logEvent.Height, expectedHeight) | |||
} | |||
doneChan <- struct{}{} | |||
}) | |||
ourVm.SetFireable(eventSwitch) | |||
var gas int64 = 100000 | |||
mstore8 := byte(MSTORE8) | |||
push1 := byte(PUSH1) | |||
log4 := byte(LOG4) | |||
stop := byte(STOP) | |||
code := []byte{ | |||
push1, 16, // data value | |||
push1, 0, // memory slot | |||
mstore8, | |||
push1, 4, // topic 4 | |||
push1, 3, // topic 3 | |||
push1, 2, // topic 2 | |||
push1, 1, // topic 1 | |||
push1, 1, // size of data | |||
push1, 0, // data starts at this offset | |||
log4, | |||
stop, | |||
} | |||
_, err = ourVm.Call(account1, account2, code, []byte{}, 0, &gas) | |||
<-doneChan | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
} |
@ -1,207 +0,0 @@ | |||
package vm | |||
import ( | |||
"crypto/rand" | |||
"encoding/hex" | |||
"fmt" | |||
"strings" | |||
"testing" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/events" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
"github.com/tendermint/tendermint/types" | |||
. "github.com/tendermint/tendermint/vm" | |||
) | |||
func newAppState() *FakeAppState { | |||
fas := &FakeAppState{ | |||
accounts: make(map[string]*Account), | |||
storage: make(map[string]Word256), | |||
} | |||
// For default permissions | |||
fas.accounts[ptypes.GlobalPermissionsAddress256.String()] = &Account{ | |||
Permissions: ptypes.DefaultAccountPermissions, | |||
} | |||
return fas | |||
} | |||
func newParams() Params { | |||
return Params{ | |||
BlockHeight: 0, | |||
BlockHash: Zero256, | |||
BlockTime: 0, | |||
GasLimit: 0, | |||
} | |||
} | |||
func makeBytes(n int) []byte { | |||
b := make([]byte, n) | |||
rand.Read(b) | |||
return b | |||
} | |||
// Runs a basic loop | |||
func TestVM(t *testing.T) { | |||
ourVm := NewVM(newAppState(), newParams(), Zero256, nil) | |||
// Create accounts | |||
account1 := &Account{ | |||
Address: Int64ToWord256(100), | |||
} | |||
account2 := &Account{ | |||
Address: Int64ToWord256(101), | |||
} | |||
var gas int64 = 100000 | |||
N := []byte{0x0f, 0x0f} | |||
// Loop N times | |||
code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)} | |||
code = append(code, N...) | |||
code = append(code, []byte{0x60, 0x20, 0x51, 0x12, 0x15, 0x60, byte(0x1b + len(N)), 0x57, 0x60, 0x01, 0x60, 0x20, 0x51, 0x01, 0x60, 0x20, 0x52, 0x60, 0x05, 0x56, 0x5B}...) | |||
start := time.Now() | |||
output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas) | |||
fmt.Printf("Output: %v Error: %v\n", output, err) | |||
fmt.Println("Call took:", time.Since(start)) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
} | |||
// Tests the code for a subcurrency contract compiled by serpent | |||
func TestSubcurrency(t *testing.T) { | |||
st := newAppState() | |||
// Create accounts | |||
account1 := &Account{ | |||
Address: LeftPadWord256(makeBytes(20)), | |||
} | |||
account2 := &Account{ | |||
Address: LeftPadWord256(makeBytes(20)), | |||
} | |||
st.accounts[account1.Address.String()] = account1 | |||
st.accounts[account2.Address.String()] = account2 | |||
ourVm := NewVM(st, newParams(), Zero256, nil) | |||
var gas int64 = 1000 | |||
code_parts := []string{"620f42403355", | |||
"7c0100000000000000000000000000000000000000000000000000000000", | |||
"600035046315cf268481141561004657", | |||
"6004356040526040515460605260206060f35b63693200ce81141561008757", | |||
"60043560805260243560a052335460c0523360e05260a05160c05112151561008657", | |||
"60a05160c0510360e0515560a0516080515401608051555b5b505b6000f3"} | |||
code, _ := hex.DecodeString(strings.Join(code_parts, "")) | |||
fmt.Printf("Code: %x\n", code) | |||
data, _ := hex.DecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005") | |||
output, err := ourVm.Call(account1, account2, code, data, 0, &gas) | |||
fmt.Printf("Output: %v Error: %v\n", output, err) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
} | |||
// Test sending tokens from a contract to another account | |||
func TestSendCall(t *testing.T) { | |||
fakeAppState := newAppState() | |||
ourVm := NewVM(fakeAppState, newParams(), Zero256, nil) | |||
// Create accounts | |||
account1 := &Account{ | |||
Address: Int64ToWord256(100), | |||
} | |||
account2 := &Account{ | |||
Address: Int64ToWord256(101), | |||
} | |||
account3 := &Account{ | |||
Address: Int64ToWord256(102), | |||
} | |||
// account1 will call account2 which will trigger CALL opcode to account3 | |||
addr := account3.Address.Postfix(20) | |||
contractCode := callContractCode(addr) | |||
//---------------------------------------------- | |||
// account2 has insufficient balance, should fail | |||
fmt.Println("Should fail with insufficient balance") | |||
exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000) | |||
if exception == "" { | |||
t.Fatal("Expected exception") | |||
} | |||
//---------------------------------------------- | |||
// give account2 sufficient balance, should pass | |||
account2.Balance = 100000 | |||
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000) | |||
if exception != "" { | |||
t.Fatal("Unexpected exception", exception) | |||
} | |||
//---------------------------------------------- | |||
// insufficient gas, should fail | |||
fmt.Println("Should fail with insufficient gas") | |||
account2.Balance = 100000 | |||
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 100) | |||
if exception == "" { | |||
t.Fatal("Expected exception") | |||
} | |||
} | |||
// subscribes to an AccCall, runs the vm, returns the exception | |||
func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas int64) string { | |||
// we need to catch the event from the CALL to check for exceptions | |||
evsw := events.NewEventSwitch() | |||
evsw.Start() | |||
ch := make(chan interface{}) | |||
fmt.Printf("subscribe to %x\n", subscribeAddr) | |||
evsw.AddListenerForEvent("test", types.EventStringAccCall(subscribeAddr), func(msg types.EventData) { | |||
ch <- msg | |||
}) | |||
evc := events.NewEventCache(evsw) | |||
ourVm.SetFireable(evc) | |||
go func() { | |||
start := time.Now() | |||
output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas) | |||
fmt.Printf("Output: %v Error: %v\n", output, err) | |||
fmt.Println("Call took:", time.Since(start)) | |||
if err != nil { | |||
ch <- err.Error() | |||
} | |||
evc.Flush() | |||
}() | |||
msg := <-ch | |||
switch ev := msg.(type) { | |||
case types.EventDataTx: | |||
return ev.Exception | |||
case types.EventDataCall: | |||
return ev.Exception | |||
case string: | |||
return ev | |||
} | |||
return "" | |||
} | |||
// this is code to call another contract (hardcoded as addr) | |||
func callContractCode(addr []byte) []byte { | |||
gas1, gas2 := byte(0x1), byte(0x1) | |||
value := byte(0x69) | |||
inOff, inSize := byte(0x0), byte(0x0) // no call data | |||
retOff, retSize := byte(0x0), byte(0x20) | |||
// this is the code we want to run (send funds to an account and return) | |||
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73} | |||
contractCode = append(contractCode, addr...) | |||
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...) | |||
return contractCode | |||
} | |||
/* | |||
// infinite loop | |||
code := []byte{0x5B, 0x60, 0x00, 0x56} | |||
// mstore | |||
code := []byte{0x60, 0x00, 0x60, 0x20} | |||
// mstore, mload | |||
code := []byte{0x60, 0x01, 0x60, 0x20, 0x52, 0x60, 0x20, 0x51} | |||
*/ |
@ -1,49 +0,0 @@ | |||
package vm | |||
import ( | |||
. "github.com/tendermint/go-common" | |||
ptypes "github.com/tendermint/tendermint/permission/types" | |||
) | |||
const ( | |||
defaultDataStackCapacity = 10 | |||
) | |||
type Account struct { | |||
Address Word256 | |||
Balance int64 | |||
Code []byte | |||
Nonce int64 | |||
Other interface{} // For holding all other data. | |||
Permissions ptypes.AccountPermissions | |||
} | |||
func (acc *Account) String() string { | |||
if acc == nil { | |||
return "nil-VMAccount" | |||
} | |||
return Fmt("VMAccount{%X B:%v C:%X N:%v}", | |||
acc.Address, acc.Balance, acc.Code, acc.Nonce) | |||
} | |||
type AppState interface { | |||
// Accounts | |||
GetAccount(addr Word256) *Account | |||
UpdateAccount(*Account) | |||
RemoveAccount(*Account) | |||
CreateAccount(*Account) *Account | |||
// Storage | |||
GetStorage(Word256, Word256) Word256 | |||
SetStorage(Word256, Word256, Word256) // Setting to Zero is deleting. | |||
} | |||
type Params struct { | |||
BlockHeight int64 | |||
BlockHash Word256 | |||
BlockTime int64 | |||
GasLimit int64 | |||
} |