SOLANA Dev Notes: Testing the solana-rpc-ruby Ruby Gem

My Solana "quick, wildman, hate-to-read-the-docs" test worked out better much better than Eth.

Here is what I did:

# install the solana cli
macos$ sh -c "$(curl -sSfL https://release.solana.com/v1.10.23/install)"

# generate and save a public key
solana-keygen pubkey
export SOLANA_DEVAPI_PUBLIC_KEY= 'use key generated above'.  #add to `.profile` if you want

# connect to the devnet cluster
solana config set --url https://api.devnet.solana.com

# install rails a new rails project just in-case this turns into a "real project"....
rails new solana
cd solana
vi Gemfile
gem 'solana_rpc_ruby'
bundle
rails g solana_rpc_ruby:install

Amazingly, all worked OK, so I wrote this quick test module:

require 'solana_rpc_ruby'

module NeoSol
  class Tests
    def self.hello_world
      account_pubkey = ENV['SOLANA_DEVAPI_PUBLIC_KEY']
      method_wrapper = SolanaRpcRuby::MethodsWrapper.new(
        cluster: 'https://api.devnet.solana.com',

        # from range 1 to 99_999 will be used
        id: rand(1..99999)
      )

    

      puts method_wrapper.cluster
      puts method_wrapper.id
      puts "Balance #{method_wrapper.get_balance(account_pubkey)}"
      pp method_wrapper.get_balance(account_pubkey)
      pp method_wrapper.api_client

      response = method_wrapper.get_account_info(account_pubkey)
      pp response
      
    end
  end
end

In the rails console:

irb(main):029:0> require "./lib/assets/test1.rb"
=> true
irb(main):030:0> NeoSol::Tests.hello_world
https://api.devnet.solana.com
37401                                                           
Balance #<SolanaRpcRuby::Response:0x000000010a4f8930>           
#<SolanaRpcRuby::Response:0x000000010a5432f0                    
 @parsed_response=                                              
  {"jsonrpc"=>"2.0",                                            
   "result"=>                                                   
    {"context"=>{"apiVersion"=>"1.10.23", "slot"=>138753442},   
     "value"=>499995000},                                       
   "id"=>37401},                                                
 @response=#<Net::HTTPOK 200 OK readbody=true>>                 
#<SolanaRpcRuby::ApiClient:0x000000010a5124e8                   
 @cluster="https://api.devnet.solana.com">                      
#<SolanaRpcRuby::Response:0x000000010a528040                    
 @parsed_response=                                              
  {"jsonrpc"=>"2.0",
   "result"=>
    {"context"=>{"apiVersion"=>"1.10.23", "slot"=>138753442},
     "value"=>
      {"data"=>"",
       "executable"=>false,
       "lamports"=>499995000,
       "owner"=>"11111111111111111111111111111111",
       "rentEpoch"=>320}},
   "id"=>37401},
 @response=#<Net::HTTPOK 200 OK readbody=true>>
=> 
#<SolanaRpcRuby::Response:0x000000010a528040
 @parsed_response=
  {"jsonrpc"=>"2.0",
   "result"=>
    {"context"=>{"apiVersion"=>"1.10.23", "slot"=>138753442},
     "value"=>{"data"=>"", "executable"=>false, "lamports"=>499995000, "owner"=>"11111111111111111111111111111111", "rentEpoch"=>320}},
   "id"=>37401},
 @response=#<Net::HTTPOK 200 OK readbody=true>>
irb(main):031:0> 

Amazing. The Solana dev environment using the solana-rpc-ruby Ruby Gem is already up and running, where I was fighting errors and scratching my head with Ethereum.

My morning coffee is not cold and my head is just now "unfuzzing" from sleep, and I have a Solana dev environment up and running in a Rails project. Thanks for this nice gem :slight_smile:

Looks like I am going to drop / table Ethereum dev testing and continue on with Solana.

Reference:

Solana CLI Install

Solana CLI Usage (to generate a pubkey and configure as needed)

Connecting to a test cluster

solana-rpc-ruby Ruby gem

Looks like a good path forward is to figure out if we can easily create "smart contracts" with Ruby, which is not directly support by Solana. Solana mentions that on-chain programes can be created with any programming language, but these programs must target the LLVM's BPF backend.

So, I found a Ruby gem, and updated the Gemfile:

gem 'rake'
gem 'ffi'
gem 'ruby-llvm', '~> 13.0', '>=13.0.2'

and install llvm on macos.

brew install llvm

After all that work, the gem compiler could not compile ruby-llvm.

An error occurred while installing ruby-llvm (13.0.2), and Bundler cannot continue.

In Gemfile:
  ruby-llvm

But after some digging, I tried this:

macos$ export PATH=/System/Volumes/Data/opt/homebrew/Cellar/llvm/13.0.1_1/bin/:$PATH

Then, bundle was OK.

...
...
Installing ruby-llvm 13.0.2 with native extensions
Bundle complete! 19 Gemfile dependencies, 79 gems now installed.
Bundled gems are installed into `./vendor/bundle`

Now that we have LLVM working in a Rails project, what's next?

References:

OK... well I seem to have run into a problem, perhaps related to the Apple MI chip, perhaps not.

macos_m1$ ruby hello.rb
/Users/tim/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/ffi-1.15.5/lib/ffi/library.rb:145:in `block in ffi_lib': Could not open library 'libLLVM-13.so.1': dlopen(libLLVM-13.so.1, 0x0005): tried: 'libLLVM-13.so.1' (no such file), '/usr/local/lib/libLLVM-13.so.1' (no such file), '/usr/lib/libLLVM-13.so.1' (no such file), '/Users/tim/rails/solana/libLLVM-13.so.1' (no such file). (LoadError)
Could not open library 'libLLVM-13.so.1.dylib': dlopen(libLLVM-13.so.1.dylib, 0x0005): tried: 'libLLVM-13.so.1.dylib' (no such file), '/usr/local/lib/libLLVM-13.so.1.dylib' (no such file), '/usr/lib/libLLVM-13.so.1.dylib' (no such file), '/Users/tim/rails/solana/libLLVM-13.so.1.dylib' (no such file).
Could not open library 'libLLVM.so.13': dlopen(libLLVM.so.13, 0x0005): tried: 'libLLVM.so.13' (no such file), '/usr/local/lib/libLLVM.so.13' (no such file), '/usr/lib/libLLVM.so.13' (no such file), '/Users/tim/rails/solana/libLLVM.so.13' (no such file).
Could not open library 'libLLVM.so.13.dylib': dlopen(libLLVM.so.13.dylib, 0x0005): tried: 'libLLVM.so.13.dylib' (no such file), '/usr/local/lib/libLLVM.so.13.dylib' (no such file), '/usr/lib/libLLVM.so.13.dylib' (no such file), '/Users/tim/rails/solana/libLLVM.so.13.dylib' (no such file).
Could not open library 'LLVM-13': dlopen(LLVM-13, 0x0005): tried: 'LLVM-13' (no such file), '/usr/local/lib/LLVM-13' (no such file), '/usr/lib/LLVM-13' (no such file), '/Users/tim/rails/solana/LLVM-13' (no such file).
Could not open library 'libLLVM-13.dylib': dlopen(libLLVM-13.dylib, 0x0005): tried: 'libLLVM-13.dylib' (no such file), '/usr/local/lib/libLLVM-13.dylib' (no such file), '/usr/lib/libLLVM-13.dylib' (no such file), '/Users/tim/rails/solana/libLLVM-13.dylib' (no such file)
	from /Users/tim/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/ffi-1.15.5/lib/ffi/library.rb:99:in `map'
	from /Users/tim/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/ffi-1.15.5/lib/ffi/library.rb:99:in `ffi_lib'
	from /Users/tim/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/ruby-llvm-13.0.2/lib/llvm/core_ffi.rb:7:in `<module:C>'
...
...
...

Searched the entire filesystem, installing llvm a number of ways, and there are no llvm shared libs anywhere to be found.

My quest to "hello_word.rb" on Solana has ran into a snag which has perplexed me for hours today.

In a hail-mary effort, now trying:

brew reinstall --build-from-source llvm

... but I do not think this will help :frowning:

Google searches yield many others with a similar issue but no joy on "other forums" to find a remedy so far.

This seems to be the core issue:

See also:

https://releases.llvm.org/11.0.1/docs/GettingStarted.html

Believe-it-or-not, that seems to have created the shared libs I need; and copying them over to /usr/local/lib and setting up the LD path env vars "seems" to have crossed that bridge.

Now, it "appears" to work


MacStudio:solana$ ruby hello.rb

; ModuleID = 'hello'
source_filename = "hello"

@hello = private unnamed_addr constant [14 x i8] c"Hello, World!\00"

; Function Attrs: nounwind
declare i32 @puts(i8* nocapture %0) #0

define i32 @main() {
  %1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @hello, i32 0, i32 0))
  ret i32 0
}

attributes #0 = { nounwind }
------------------------------
Hello, World!

LOL, not really sure this test "worked" or not; but seems like progress :slight_smile:

Editorial note:

# http://llvm.org/docs/LangRef.html#module-structure
require '/Users/tim/rails/solana/vendor/bundle/ruby/3.0.0/gems/ruby-llvm-13.0.2/lib/llvm/core'
require '/Users/tim/rails/solana/vendor/bundle/ruby/3.0.0/gems/ruby-llvm-13.0.2/lib/llvm/execution_engine'

HELLO_STRING = "Hello, World!"

# modules hold functions and variables
mod = LLVM::Module.new('hello')

# Declare the string constant as a global constant.
hello = mod.globals.add(LLVM::ConstantArray.string(HELLO_STRING) , :hello) do |var|
  var.linkage = :private
  var.global_constant = true
  var.unnamed_addr = true
  var.initializer = LLVM::ConstantArray.string(HELLO_STRING)
end

# External declaration of the `puts` function
cputs = mod.functions.add('puts', [LLVM.Pointer(LLVM::Int8)], LLVM::Int32) do |function, string|
  function.add_attribute :no_unwind_attribute
  string.add_attribute :no_capture_attribute
end

# Definition of main function
# a function is made up of connected BasicBlocks and must have _one entry and exit
# basic blocks are (mostly) simple machine instructions and can be connected in a graph
main = mod.functions.add('main', [], LLVM::Int32) do |function|
  function.basic_blocks.append.build do |b|
    zero = LLVM.Int(0) # a LLVM Constant value

    # Read here what GetElementPointer (gep) means http://llvm.org/releases/3.2/docs/GetElementPtr.html
    # Convert [13 x i8]* to i8  *...
    cast210 = b.gep hello, [zero, zero], 'cast210'
    # Call puts function to write out the string to stdout.
    b.call cputs, cast210
    b.ret zero
  end
end

mod.dump
#mod.dispose
puts "------------------------------"

LLVM.init_jit

engine = LLVM::JITCompiler.new(mod)
engine.run_function(main)
engine.dispose

Hmmm. Seems, perhaps, maybe OK?

So, this is the first building block on how to write "smart contracts" on the Solana blockchain ?

If so, it's not wonder crypto deapps developers make the big bucks!

:slight_smile:

Question to self ... "Why do I never ask questions on forums? Because when I do, I never get a reply?"

After sleeping on this, I think I will move away from this path of compiling on-chain programs for Solana using LLVM.

This type of low level programming is not my cup-of-tea (not enjoyable for me). I prefer programming in Ruby these days and going down the path of on-chain programs is a totally different direction.

For anyone who wishes to go down this Solana on-chain programs path, see:

Final Thoughts

It seems to me these on-chain programs may be a bit to hard to develop in the current state-of-the-art and that there needs to be a "better way" to do this if on-claim programs, often referred to as "smart contracts" are going to succeed, in the long term.

Maybe I am wrong?

So far, I have worked quickly with Ethereum and Solana; and after going down the rabbit hole with both blockchains on-chain program development environments, I simply lost interest.

I found LLVM to be awkward and it felt "antique" to me and my feeling was that it was a kind of kludge. It sounds very interesting "on-chain programs", but the way it has been implemented for Solana could not hold my interest.

Of course, I am often wrong, so perhaps I am wrong on this and it is simply my lack of interest in writing code at this low of a level?

Either way, that is the end of my analysis down the Solana rabbit hole for now. I'll look around and decide what to explore next in the blockchain analysis space.

Others have advised me to use more "crypto mainstream" programming languages, since Ruby has fallen out of favor and not used very much in the cryptoverse these days.

Interesting historical article from December 2021:

Solana suffers network slowdown, raising questions around proof of history