jvm如何确定服务器_JVM上的确定性执行

jvm如何确定服务器

重要要点

  • 目前使用的加密货币不适合通用支付网络
  • 带有密码验证的分布式共享分类帐对于一定数量的技术用例而言似乎是潜在有用的工具
  • 将共享分类帐与用于编写对共享分类帐状态起作用的“智能合约”的编程框架捆绑在一起似乎很有希望。
  • 构造这种智能合约语言几乎会立即遇到基本的计算机科学问题
  • JVM和类加载为规避这些问题并提供可证明的确定性执行智能合约提供了良好的基础

现在,许多开发人员都听说过比特币和更广泛的加密货币领域。 关于非法交易和犯罪活动的卢里德故事在媒体上已经出现,而比特币一直是其中许多故事的核心。

现在越来越清楚的是,比特币的主要属性(公共匿名交易记录,不可逆交易和不信任账户网络)非常不适用于日常合法交易的支付网络。

但是,从纯粹的技术角度来看,加密货币的发展包含了几个有趣的并且可能是新颖的方面。 这些方面的合法用例才刚刚开始开发。 随着比特币和其他加密货币的技术方面被分解和纠缠,一些非常有趣的技术工作即将出现。

这些类型的系统在诸如供应链管理,金融技术(包括清算系统)和所有权登记册(特别是对于可能存在记录保存或腐败问题的司法管辖区中的有形资产)等行业显示出希望。 2016年的“区块链”嗡嗡声已经在逐渐消失,但这允许该技术出现实际用例,而无需大肆宣传。

特别令人感兴趣的是在Linux基金会的主持下运行的Hyperledger项目 ,该项目使大约80个组织对分布式账本,区块链和相关的加密货币技术有着共同的兴趣。

在本文中,我们希望专注于这一新兴领域的特定技术方面,因此让我们考虑共享账本中的所有参与者都是彼此已知的情况(也许通过让一些中央受信任的第三方向参与者分发已知密钥对) )。 这消除了作为分类帐功能的不信任,并意味着也可以丢弃公共采矿(以及相关的极其浪费的工作量证明方面)。

即使在这种简化的情况下,仍然存在一些重大问题。 我们将只关注其中之一-网络中的每个参与者都必须确保,如果他们执行智能合约,该问题将对分类账产生相同的影响(假设所有参与者都从同一分类账状态开始) )。

换句话说,对于健壮的智能合约框架,我们需要确保在框架中编写的任何合约都将确定性地执行并终止。 这是对这个问题的重述,该问题已经在理论计算机科学领域进行了很长时间的研究。

每个CS本科生都知道,图灵机是为计算机程序执行建模的常用理论基础。 该理论的通常方法是显示特定的图灵机和在物理实现的数字计算机上运行的等效程序之间存在对应关系。

因此,可以将编程语言归类为图灵完整的语言,该语言可以模拟所有可能的图灵机(标准假设是可以将内存视为无限的)和其他所有语言。 非图灵完整语言显然不如图灵完整语言强大,因此计算机科学家对它们的兴趣大大降低。

这种分类引人注目且有用,尽管它立即暴露了计算机科学的基本问题之一- 停机问题 。 松散地,这是最初归因于Alan Turing的证明,无法构造通用算法来确定Turing机器在给定初始状态下是否将永远终止或运行。

乍一看,这对确定性执行的概念构成了严重的障碍-我们如何确保即使在一般情况下都无法确定诸如停止行为之类的基本内容时,程序的所有运行都会产生相同的结果? 幸运的是,我们可以采用各种技术来避免停机问题及其后果。

首先,我们应该注意,图灵机的定义专门指的是无限存储带(在简单的等效证明中被建模为无限存储)。 当然,真正的数字计算机在范围上是有限的。 这应该给我们一些希望,我们可以通过限制程序大小或以某种方式计算来避免停止问题。 进一步的线索来自函数式编程。

如果我们认为程序是由有限状态从X到Y的单个转换表示的(例如,该状态可以视为有限图灵机存储磁带的状态),则可以将其表示为:

f: X -> Y

现在,如果我们考虑所有可能程序的空间(我们认为是有限的),并且在程序未在一定时限内终止的情况下,将特殊状态添加到目标空间中,那么我们可以建立一个有限状态(但非常大)地图。

该映射表示所有可能的程序执行。 它的键是(start_state,program_code)对,其值是通过将程序应用于start_state所获得的终端状态。 这是称为记忆的函数式编程技术,因为在没有副作用的语言中,我们可以用预先计算的结果替换应用函数的动作。 这也表明,对于有限的状态空间和有限的程序,可以避免停止。

不仅如此,这种绕开功能思维的方式还为确定性执行提供了有用的正式定义:

确定性执行意味着给定状态X和函数f,则将f应用于X的任何应用将在有限时间内产生相同的状态Y,而不管谁应用该函数(假定执行平台的语义一致)

注意,为了使确定性执行有用,我们还需要一些编程方法来确定给定函数是否是确定性的,而不执行代码。

有了基本理论,建立了,我们现在转向实际的例子在分布式总账-从确定性的classloader 琴弦项目 (最近捐赠给Hyperledger项目)。 这提供了一个JVM框架,用于编写确定性智能合约,该合约在分布式,加密签名的分类帐的状态下运行。

可以在Github上Corda存储库的实验部分找到该代码。 欢迎参加,讨论和提出要求。

乍一看,对于确定性执行平台,JVM似乎是一个潜在的奇怪选择。 JVM的许多方面都是高度动态的-例如,所有Java程序的固有多线程处理,不确定性垃圾回收,JIT编译中的竞争条件导致在不同运行时产生不同的编译方法集等。

但是,JVM具有许多优点:

  • JVM字节码语义非常好理解并且易于处理
  • Java安全模型已经过详细检查和攻击,并且功能强大
  • JVM字节码的工具空间非常成熟
  • 类加载为分析代码以确定性提供了便利的平台和机会

Corda沙箱采用的方法是同时使用类加载和运行时组件来实现以下目的:

  • 拒绝任何明显的不确定性程序
  • 在类加载时将资源跟踪代码插入候选程序
  • 提供一个简单的系统来杀死任何试图违反资源限制的程序(并回滚任何事务)

WhitelistClassLoader是一个类加载器,它执行确定性框架的类加载过程。 例如,它将拒绝任何直接或间接创建新线程的代码,因为所有多线程程序本质上都是不确定的。

类加载器包含一个白名单(因此具有名称),该白名单包含一个已知安全的方法列表,例如Object :: getClass(),这些方法被允许调用(即使可以通过其他方式排除它们,例如是否包含本地代码)。

列入白名单的加载器还将在加载时将运行时跟踪代码注入类中,以允许运行时系统检测可能占用大量资源的类。

类加载系统还知道基本JRE库中的哪些方法是确定性的,并且这些方法可以由用户代码自由调用。 有了这些组件,就可以构建一个确定性执行智能合约代码的系统。

类加载器的用户在加载类时以通常的方式调用loadClass()方法。 此方法尝试在缓存中定位该类,如果找不到该类,则允许父类加载器尝试加载它。 如果两者均失败,则类加载器将委派给findClass()。

注意

这只是遵循Java类加载的标准模式。 自定义类加载器通常会覆盖findClass()方法以实现其自定义加载功能。

这意味着自定义类加载的真正内容发生在findClass()中。 对于列入白名单的类加载器,此方法的核心是扫描以查看该类是否为确定性类。 该方法使用ASM处理类读取,并使用访问者模式检查类。 扫描代码(略有简化)如下所示:

public boolean scan() throws IOException {
  try (final InputStream in = Files.newInputStream(
               classDir.resolve(classInternalName + ".class"))) {
    try {
      // Create an ASM class reader from the stream
      final ClassReader classReader = new ClassReader(in);

      // Make a class visitor to analyse the class and
      // ensure determinism
      // We use a CandidacyStatus object to keep track of methods
      // we encounter when scanning the class, and to mark them
      // as deterministic or not.
      ClassVisitor whitelistCheckingClassVisitor
        = new WhitelistCheckingClassVisitor(classInternalName, 
                                            candidacyStatus);

      // Use ASM's reader / visitor architecture
      // to visit the class we're trying to load
      classReader.accept(whitelistCheckingClassVisitor, 
                         ClassReader.SKIP_DEBUG);
    } catch (Exception ex) {
      // ... Exception handling omitted
    }
  }

  return candidacyStatus.isLoadable();
}

类加载过程的关键是类访问者。 这负责对代码施加某些约束。

例如,strictfp修饰符是Java语言的鲜为人知且很少使用的角落,它强制严格遵守IEEE 754浮点标准。 通常,很少有此类设置该类,但是如果没有strictfp关键字,JVM通常将在硬件中计算浮点数学,其精度将取决于可用的硬件。

在现代CPU上,JVM通常会提供比IEEE 754要求的结果更为精确的结果,但是当然,这意味着不同的实现可能会根据硬件产生不同的结果。 这是不确定性的潜在来源,因此必须加以解决。

确定性类加载器可以简单地拒绝任何不包含strictfp作为说明符的程序,但是由于很少有程序员使用它,这简直令人沮丧。 取而代之的是,白名单类加载器只是在加载所有类时对所有类无提示地打开strictfp,以确保行为一致。

在转向分析的主要部分之前,类访问者的实现会处理strictfp和其他各种确定性极端情况(例如,不允许使用终结器的任何类)。 这是依次使用每种方法使用WhitelistCheckingMethodVisitor来访问每种方法。

在整个扫描过程中,类加载器使用一种称为CandidacyStatus的类型,该类型表示对特定方法集(通常是单个类的方法及其依赖项)的确定性状态的了解。 该方法集被认为是在确定性上下文中可加载的“候选”,因此得名。

WhitelistCheckingMethodVisitor的主要工作是建立一个调用图,该调用图的方法是从候选类中的方法中调用的。 基本规则是,如果候选方法仅调用确定性方法,那么它将是确定性的,一旦我们扫描了整个类,我们构建的调用图将帮助我们确定整个类是否是确定性的。

在代码中,这可以通过覆盖ASM Visitor API中的visitMethodInsn()方法相对简单地完成。

public void visitMethodInsn(int opcode, String owner, String name, 
                            String desc, boolean itf) {

  CandidateMethod candidateMethod = 
              candidacyStatus.getCandidateMethod(currentMethodName);
  String internalName = owner + "." + name + ":" + desc;
  if (candidacyStatus.putIfAbsent(internalName)) {
    candidacyStatus.addToBacklog(internalName);
  }
  CandidateMethod referencedCandidateMethod = 
              candidacyStatus.getCandidateMethod(internalName);
  candidateMethod.addReferencedCandidateMethod(referencedCandidateMethod);
      // ...
}

方法访问者还需要一些特殊情况和家政服务。 例如,运行时使用ThreadDeath错误杀死所有超过运行时处理限制的线程。 因此,尝试捕获ThreadDeath的代码可以尝试规避确定性检查。

最简单的解决方案是让方法访问者简单地禁止捕获ThreadDeath或其超类(Error或Throwable)的任何类。 每当访问try-catch块时,都应调用实现此目的的代码:

public void visitTryCatchBlock(Label start, Label end, 
                               Label handler, String type) {
  if (type == null)
    throw new IllegalArgumentException("Exception type must " + 
            "not be null in try/catch block in " + currentMethodName);

    // Forcible disallow attempts to catch ThreadDeath or any 
    //  throwable superclass - preserve determinism
    if (type.equals(Utils.THREAD_DEATH) || type.equals(Utils.ERROR)
                    || type.equals(Utils.THROWABLE)) {
      CandidateMethod candidateMethod = 
             candidacyStatus.getCandidateMethod(currentMethodName);
      candidateMethod.disallowed("Method " + currentMethodName + 
             " attempts to catch ThreadDeath, Error or Throwable");
    }
  }
}

确定单个方法的调用图后,简单分析表明,如果某个方法:

  • 不是明确不确定的
  • 只调用确定性方法

那么该方法是确定性的。

完成对单个方法的扫描后,控制权返回给类访问者。 如果已扫描所有方法,它将检查是否已将该类中的所有方法标记为确定性的。 如果是这样,则将该类标记为可加载,然后在加载之前对其进行运行时资源检查。

白名单框架非常新,但是早期测试令人鼓舞,它似乎提供了确定性执行的方法,该方法不仅适用于新移民,而且为构建需要确定性的可靠保证的生产系统奠定了坚实的基础。

翻译自: https://www.infoq.com/articles/Deterministic-Execution-JVM/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

jvm如何确定服务器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值